This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit f5b63cea18eae90af81575a2dfd82344f501922c
Author: Eren Terzioglu <eren.terzio...@espressif.com>
AuthorDate: Thu Jul 11 14:36:50 2024 +0200

    esp32[c3|c6|h2]: Add SPI master DMA support
---
 arch/risc-v/src/common/espressif/Kconfig   |  23 ++
 arch/risc-v/src/common/espressif/esp_spi.c | 343 +++++++++++++++++++++++++++--
 2 files changed, 352 insertions(+), 14 deletions(-)

diff --git a/arch/risc-v/src/common/espressif/Kconfig 
b/arch/risc-v/src/common/espressif/Kconfig
index e03cac2620..85099a09b2 100644
--- a/arch/risc-v/src/common/espressif/Kconfig
+++ b/arch/risc-v/src/common/espressif/Kconfig
@@ -907,6 +907,29 @@ config ESPRESSIF_SPI_UDCS
 
 if ESPRESSIF_SPI2
 
+config ESPRESSIF_SPI2_DMA
+       bool "SPI2 use GDMA"
+       default n
+       depends on ESPRESSIF_DMA
+       ---help---
+               Enable support for transfers using the GDMA engine.
+
+config ESPRESSIF_SPI2_DMADESC_NUM
+       int "SPI2 Master GDMA maximum number of descriptors"
+       default 2
+       depends on ESPRESSIF_SPI2_DMA
+       ---help---
+               Configure the maximum number of out-link/in-link descriptors to
+               be chained for a GDMA transfer.
+
+config ESPRESSIF_SPI2_DMATHRESHOLD
+       int "SPI2 GDMA threshold"
+       default 64
+       depends on ESPRESSIF_SPI2_DMA
+       ---help---
+               When SPI GDMA is enabled, GDMA transfers whose size are below 
the
+               defined threshold will be performed by polling logic.
+
 config ESPRESSIF_SPI2_SLAVE
        bool "SPI2 Slave mode"
        default n
diff --git a/arch/risc-v/src/common/espressif/esp_spi.c 
b/arch/risc-v/src/common/espressif/esp_spi.c
index dd14fd0334..e4f29634fe 100644
--- a/arch/risc-v/src/common/espressif/esp_spi.c
+++ b/arch/risc-v/src/common/espressif/esp_spi.c
@@ -48,6 +48,10 @@
 #include "esp_irq.h"
 #include "esp_gpio.h"
 
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+#include "esp_dma.h"
+#endif
+
 #include "riscv_internal.h"
 
 #include "hal/spi_hal.h"
@@ -69,6 +73,11 @@
 #  define MISO_PIN_ATTR (INPUT_FUNCTION_2 | PULLUP)
 #endif
 
+#if defined(CONFIG_ARCH_CHIP_ESP32C6) || defined(CONFIG_ARCH_CHIP_ESP32H2)
+#  define SPI2_INTR_SOURCE GSPI2_INTR_SOURCE
+#  define ESP_IRQ_SPI2     ESP_IRQ_GSPI2
+#endif
+
 #define SPI2_IOMUX_MISOPIN  2
 #define SPI2_IOMUX_MOSIPIN  7
 #define SPI2_IOMUX_CLKPIN   6
@@ -82,6 +91,14 @@
 #  define SPI_HAVE_SWCS FALSE
 #endif
 
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+
+/* SPI DMA RX/TX number of descriptors */
+
+#define SPI_DMA_DESC_NUM    (CONFIG_ESPRESSIF_SPI2_DMADESC_NUM)
+
+#endif
+
 /* Verify whether SPI has been assigned IOMUX pins.
  * Otherwise, SPI signals will be routed via GPIO Matrix.
  */
@@ -116,19 +133,23 @@
 
 struct esp_spi_config_s
 {
-  uint32_t width;             /* SPI default width */
-  uint8_t cs_pin;             /* GPIO configuration for CS */
-  uint8_t mosi_pin;           /* GPIO configuration for MOSI */
-  uint8_t miso_pin;           /* GPIO configuration for MISO */
-  uint8_t clk_pin;            /* GPIO configuration for CLK */
-  uint32_t cs_insig;          /* SPI CS input signal index */
-  uint32_t cs_outsig;         /* SPI CS output signal index */
-  uint32_t mosi_insig;        /* SPI MOSI input signal index */
-  uint32_t mosi_outsig;       /* SPI MOSI output signal index */
-  uint32_t miso_insig;        /* SPI MISO input signal index */
-  uint32_t miso_outsig;       /* SPI MISO output signal index */
-  uint32_t clk_insig;         /* SPI CLK input signal index */
-  uint32_t clk_outsig;        /* SPI CLK output signal index */
+  uint32_t width;              /* SPI default width */
+  uint8_t  cs_pin;             /* GPIO configuration for CS */
+  uint8_t  mosi_pin;           /* GPIO configuration for MOSI */
+  uint8_t  miso_pin;           /* GPIO configuration for MISO */
+  uint8_t  clk_pin;            /* GPIO configuration for CLK */
+  uint32_t cs_insig;           /* SPI CS input signal index */
+  uint32_t cs_outsig;          /* SPI CS output signal index */
+  uint32_t mosi_insig;         /* SPI MOSI input signal index */
+  uint32_t mosi_outsig;        /* SPI MOSI output signal index */
+  uint32_t miso_insig;         /* SPI MISO input signal index */
+  uint32_t miso_outsig;        /* SPI MISO output signal index */
+  uint32_t clk_insig;          /* SPI CLK input signal index */
+  uint32_t clk_outsig;         /* SPI CLK output signal index */
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+  uint8_t  periph;             /* Peripheral ID */
+  uint8_t  irq;                /* Interrupt ID */
+#endif
 };
 
 struct esp_spi_priv_s
@@ -142,6 +163,11 @@ struct esp_spi_priv_s
   const struct esp_spi_config_s *config;
   int refs;                             /* Reference count */
   mutex_t lock;                         /* Held while chip is selected for 
mutual exclusion */
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+  sem_t sem_isr;                        /* Interrupt wait semaphore */
+  int cpuint;                           /* SPI interrupt ID */
+  int32_t dma_channel;                  /* Channel assigned by the GDMA driver 
*/
+#endif
   uint8_t nbits;                        /* Actual SPI send/receive bits once 
transmission */
   uint8_t id;                           /* ID number of SPI interface */
   uint8_t module;                       /* Module ID of SPI interface */
@@ -173,10 +199,19 @@ static uint32_t esp_spi_send(struct spi_dev_s *dev, 
uint32_t wd);
 static void esp_spi_exchange(struct spi_dev_s *dev,
                              const void *txbuffer,
                              void *rxbuffer, size_t nwords);
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+static int esp_spi_interrupt(int irq, void *context, void *arg);
+static int esp_spi_sem_waitdone(struct esp_spi_priv_s *priv);
+static void esp_spi_dma_exchange(struct esp_spi_priv_s *priv,
+                                     const void *txbuffer,
+                                     void *rxbuffer,
+                                     uint32_t nwords);
+#else
 static void esp_spi_poll_exchange(struct esp_spi_priv_s *priv,
                                   const void *txbuffer,
                                   void *rxbuffer,
                                   size_t nwords);
+#endif
 #ifndef CONFIG_SPI_EXCHANGE
 static void esp_spi_sndblock(struct spi_dev_s *dev,
                              const void *txbuffer,
@@ -188,6 +223,9 @@ static void esp_spi_recvblock(struct spi_dev_s *dev,
 #ifdef CONFIG_SPI_TRIGGER
 static int esp_spi_trigger(struct spi_dev_s *dev);
 #endif
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+static void esp_spi_dma_init(struct spi_dev_s *dev);
+#endif
 static void esp_spi_init(struct spi_dev_s *dev);
 static void esp_spi_deinit(struct spi_dev_s *dev);
 
@@ -244,6 +282,10 @@ static const struct esp_spi_config_s esp_spi2_config =
   .mosi_pin     = CONFIG_ESPRESSIF_SPI2_MOSIPIN,
   .miso_pin     = CONFIG_ESPRESSIF_SPI2_MISOPIN,
   .clk_pin      = CONFIG_ESPRESSIF_SPI2_CLKPIN,
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+  .periph       = SPI2_INTR_SOURCE,
+  .irq          = ESP_IRQ_SPI2,
+#endif
   .cs_insig     = FSPICS0_IN_IDX,
   .cs_outsig    = FSPICS0_OUT_IDX,
   .mosi_insig   = FSPID_IN_IDX,
@@ -301,9 +343,23 @@ static struct esp_spi_priv_s esp_spi2_priv =
   .dev_cfg      = &dev_cfg,
   .ctx          = &ctx,
   .timing_param = &timing_param,
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+  .sem_isr      = SEM_INITIALIZER(0),
+  .cpuint       = -ENOMEM,
+  .dma_channel  = -1,
+#endif
 };
 #endif /* CONFIG_ESPRESSIF_SPI2 */
 
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+
+/* SPI DMA RX/TX description */
+
+static struct esp_dmadesc_s dma_rxdesc[SPI_DMA_DESC_NUM];
+static struct esp_dmadesc_s dma_txdesc[SPI_DMA_DESC_NUM];
+
+#endif
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -340,6 +396,27 @@ static int esp_spi_lock(struct spi_dev_s *dev, bool lock)
   return ret;
 }
 
+/****************************************************************************
+ * Name: esp_spi_sem_waitdone
+ *
+ * Description:
+ *   Wait for a transfer to complete.
+ *
+ * Input Parameters:
+ *   priv - SPI private state data
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+static int esp_spi_sem_waitdone(struct esp_spi_priv_s *priv)
+{
+  return nxsem_tickwait_uninterruptible(&priv->sem_isr, SEC2TICK(10));
+}
+#endif
+
 /****************************************************************************
  * Name: esp_spi_select
  *
@@ -509,6 +586,114 @@ static int esp_spi_hwfeatures(struct spi_dev_s *dev,
 }
 #endif
 
+/****************************************************************************
+ * Name: esp_spi_dma_exchange
+ *
+ * Description:
+ *   Exchange a block of data from SPI by DMA.
+ *
+ * Input Parameters:
+ *   priv     - SPI private state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to the buffer in which to receive data
+ *   nwords   - the length of data that to be exchanged in units of words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+static void esp_spi_dma_exchange(struct esp_spi_priv_s *priv,
+                                     const void *txbuffer,
+                                     void *rxbuffer,
+                                     uint32_t nwords)
+{
+  const uint32_t total = nwords * (priv->nbits / 8);
+  const int32_t channel = priv->dma_channel;
+  uint32_t bytes = total;
+  uint32_t n;
+  uint8_t *tp = (uint8_t *)txbuffer;
+  uint8_t *rp = (uint8_t *)rxbuffer;
+  spi_hal_trans_config_t trans =
+    {
+      0
+    };
+
+  if (tp == NULL)
+    {
+      tp = rp;
+    }
+
+  spi_hal_setup_device(priv->ctx, priv->dev_cfg);
+  spi_ll_clear_int_stat(priv->ctx->hw);
+
+  trans.cs_keep_active = priv->dev_cfg->cs_hold;
+  trans.line_mode.data_lines = 2;
+  trans.line_mode.addr_lines = 1;
+  trans.line_mode.cmd_lines = 1;
+
+  while (bytes != 0)
+    {
+      /* Reset SPI DMA TX FIFO */
+
+      spi_ll_cpu_rx_fifo_reset(priv->ctx->hw);
+
+      /* Enable SPI DMA TX */
+
+      spi_ll_dma_tx_fifo_reset(priv->ctx->hw);
+      spi_ll_dma_tx_enable(priv->ctx->hw, 1);
+
+      n = esp_dma_setup(channel, true, dma_txdesc,
+                        SPI_DMA_DESC_NUM, tp, bytes);
+      esp_dma_enable(channel, true);
+
+      /* Write data words to data buffer registers.
+       * SPI peripheral contains 16 registers (W0 - W15).
+       */
+
+      trans.tx_bitlen = n * 8;
+      trans.rx_bitlen = n * 8;
+      trans.rcv_buffer = (uint8_t *)rp;
+      trans.send_buffer = (uint8_t *)tp;
+      priv->ctx->trans_config = trans;
+
+      spi_ll_set_mosi_bitlen(priv->ctx->hw, (n * 8));
+      spi_ll_enable_mosi(priv->ctx->hw, true);
+
+      tp += n;
+
+      if (rp != NULL)
+        {
+          /* Enable SPI DMA RX */
+
+          spi_ll_dma_rx_enable(priv->ctx->hw, 1);
+          esp_dma_setup(channel, false, dma_rxdesc, SPI_DMA_DESC_NUM,
+                        rp, bytes);
+          esp_dma_enable(channel, false);
+          spi_ll_enable_miso(priv->ctx->hw, true);
+
+          rp += n;
+        }
+      else
+        {
+          spi_ll_enable_miso(priv->ctx->hw, false);
+        }
+
+      spi_ll_master_user_start(priv->ctx->hw);
+
+      esp_spi_sem_waitdone(priv);
+      bytes -= n;
+    }
+
+  spi_ll_clear_int_stat(priv->ctx->hw);
+}
+#endif
+
 /****************************************************************************
  * Name: esp_spi_poll_send
  *
@@ -681,7 +866,18 @@ static void esp_spi_exchange(struct spi_dev_s *dev,
 {
   struct esp_spi_priv_s *priv = (struct esp_spi_priv_s *)dev;
 
-  esp_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords);
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+  size_t thld = CONFIG_ESPRESSIF_SPI2_DMATHRESHOLD;
+
+  if (nwords > thld)
+    {
+      esp_spi_dma_exchange(priv, txbuffer, rxbuffer, nwords);
+    }
+  else
+#endif
+    {
+      esp_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords);
+    }
 }
 
 #ifndef CONFIG_SPI_EXCHANGE
@@ -768,6 +964,41 @@ static int esp_spi_trigger(struct spi_dev_s *dev)
 }
 #endif
 
+/****************************************************************************
+ * Name: esp_spi_dma_init
+ *
+ * Description:
+ *   Initialize SPI connection to GDMA engine.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+void esp_spi_dma_init(struct spi_dev_s *dev)
+{
+  struct esp_spi_priv_s *priv = (struct esp_spi_priv_s *)dev;
+
+  /* Initialize GDMA controller */
+
+  esp_dma_init();
+
+  /* Request a GDMA channel for SPI peripheral */
+
+  priv->dma_channel = esp_dma_request(ESPRESSIF_DMA_PERIPH_M2M, 1, 1, true);
+  if (priv->dma_channel < 0)
+    {
+      spierr("Failed to allocate GDMA channel\n");
+
+      DEBUGPANIC();
+    }
+}
+#endif
+
 /****************************************************************************
  * Name: esp_spi_init
  *
@@ -828,7 +1059,22 @@ static void esp_spi_init(struct spi_dev_s *dev)
 
   periph_module_enable(priv->module);
 
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+  esp_spi_dma_init(dev);
+
+  priv->ctx->hw = SPI_LL_GET_HW(priv->id);
+  priv->cfg->dma_enabled = true;
+  priv->cfg->dmadesc_rx = (lldesc_t *)dma_rxdesc;
+  priv->cfg->dmadesc_tx = (lldesc_t *)dma_txdesc;
+  priv->cfg->rx_dma_chan = priv->dma_channel;
+  priv->cfg->tx_dma_chan = priv->dma_channel;
+
+  spi_ll_master_init(priv->ctx->hw);
+  spi_ll_enable_int(priv->ctx->hw);
+  spi_ll_set_mosi_delay(priv->ctx->hw, 0, 0);
+#else
   spi_hal_init(priv->ctx, priv->id, priv->cfg);
+#endif
 
   priv->dev_cfg->timing_conf.clock_source = SPI_CLK_SRC_DEFAULT;
 
@@ -863,6 +1109,34 @@ static void esp_spi_deinit(struct spi_dev_s *dev)
   priv->nbits     = 0;
 }
 
+/****************************************************************************
+ * Name: esp_spi_interrupt
+ *
+ * Description:
+ *   Common SPI DMA interrupt handler.
+ *
+ * Input Parameters:
+ *   irq     - Number of the IRQ that generated the interrupt
+ *   context - Interrupt register state save info
+ *   arg     - SPI controller private data
+ *
+ * Returned Value:
+ *   Standard interrupt return value.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+static int esp_spi_interrupt(int irq, void *context, void *arg)
+{
+  struct esp_spi_priv_s *priv = (struct esp_spi_priv_s *)arg;
+
+  spi_ll_clear_intr(priv->ctx->hw, SPI_LL_INTR_TRANS_DONE);
+  nxsem_post(&priv->sem_isr);
+
+  return 0;
+}
+#endif
+
 /****************************************************************************
  * Name: esp_spibus_initialize
  *
@@ -903,6 +1177,41 @@ struct spi_dev_s *esp_spibus_initialize(int port)
       return spi_dev;
     }
 
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+  if (priv->cpuint != -ENOMEM)
+    {
+      /* Disable the provided CPU Interrupt to configure it. */
+
+      up_disable_irq(priv->config->irq);
+    }
+
+  priv->cpuint = esp_setup_irq(priv->config->periph,
+                               ESP_IRQ_PRIORITY_DEFAULT,
+                               ESP_IRQ_TRIGGER_LEVEL);
+  if (priv->cpuint < 0)
+    {
+      /* Failed to allocate a CPU interrupt of this type. */
+
+      nxmutex_unlock(&priv->lock);
+      return NULL;
+    }
+
+  if (irq_attach(priv->config->irq, esp_spi_interrupt, priv) != OK)
+    {
+      /* Failed to attach IRQ, so CPU interrupt must be freed. */
+
+      esp_teardown_irq(priv->config->periph, priv->cpuint);
+      priv->cpuint = -ENOMEM;
+      nxmutex_unlock(&priv->lock);
+
+      return NULL;
+    }
+
+  /* Enable the CPU interrupt that is linked to the SPI device. */
+
+  up_enable_irq(priv->config->irq);
+#endif
+
   esp_spi_init(spi_dev);
   priv->refs++;
 
@@ -942,6 +1251,12 @@ int esp_spibus_uninitialize(struct spi_dev_s *dev)
       return OK;
     }
 
+#ifdef CONFIG_ESPRESSIF_SPI2_DMA
+  up_disable_irq(priv->config->irq);
+  esp_teardown_irq(priv->config->periph, priv->cpuint);
+  priv->cpuint = -ENOMEM;
+#endif
+
   esp_spi_deinit(dev);
   nxmutex_unlock(&priv->lock);
 

Reply via email to