You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/08/09 23:10:13 UTC

[GitHub] [incubator-nuttx] acassis opened a new pull request, #6823: xtensa/esp32s2: Add basic support to SPI

acassis opened a new pull request, #6823:
URL: https://github.com/apache/incubator-nuttx/pull/6823

   ## Summary
   Add basic support to SPI
   ## Impact
   Only ESP32-S2
   ## Testing
   ESP32S2-SAOLA-1


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] gustavonihei commented on a diff in pull request #6823: xtensa/esp32s2: Add basic support to SPI

Posted by GitBox <gi...@apache.org>.
gustavonihei commented on code in PR #6823:
URL: https://github.com/apache/incubator-nuttx/pull/6823#discussion_r942389623


##########
arch/xtensa/src/esp32s2/esp32s2_spi.c:
##########
@@ -0,0 +1,1231 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32s2/esp32s2_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_ESP32S2_SPI
+
+#include <assert.h>
+#include <debug.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/spi/spi.h>
+
+#include <arch/board/board.h>
+
+#include "esp32s2_spi.h"
+#include "esp32s2_irq.h"
+#include "esp32s2_gpio.h"
+
+#include "xtensa.h"
+#include "hardware/esp32s2_gpio_sigmap.h"
+#include "hardware/esp32s2_pinmap.h"
+#include "hardware/esp32s2_spi.h"
+#include "hardware/esp32s2_soc.h"
+#include "hardware/esp32s2_system.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Check if Chip-Select pin will be controlled via software */
+
+#ifdef CONFIG_ESP32S2_SPI_SWCS
+#  define SPI_HAVE_SWCS 1
+#else
+#  define SPI_HAVE_SWCS 0
+#endif
+
+/* SPI default frequency (limited by clock divider) */
+
+#define SPI_DEFAULT_FREQ  (400000)
+
+/* SPI default width */
+
+#define SPI_DEFAULT_WIDTH (8)
+
+/* SPI default mode */
+
+#define SPI_DEFAULT_MODE  (SPIDEV_MODE0)
+
+/* Helper for applying the mask for a given register field.
+ * Mask is determined by the macros suffixed with _V and _S from the
+ * peripheral register description.
+ */
+
+#define VALUE_MASK(_val, _field) (((_val) & (_field##_V)) << (_field##_S))

Review Comment:
   ```suggestion
   ```
   There is a similar macro named `VALUE_TO_FIELD` already defined in `esp32s2_soc.h`, no need to define another one here.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] gustavonihei commented on a diff in pull request #6823: xtensa/esp32s2: Add basic support to SPI

Posted by GitBox <gi...@apache.org>.
gustavonihei commented on code in PR #6823:
URL: https://github.com/apache/incubator-nuttx/pull/6823#discussion_r942511267


##########
arch/xtensa/src/esp32s2/esp32s2_spi.c:
##########
@@ -0,0 +1,1231 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32s2/esp32s2_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_ESP32S2_SPI
+
+#include <assert.h>
+#include <debug.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/spi/spi.h>
+
+#include <arch/board/board.h>
+
+#include "esp32s2_spi.h"
+#include "esp32s2_irq.h"
+#include "esp32s2_gpio.h"
+
+#include "xtensa.h"
+#include "hardware/esp32s2_gpio_sigmap.h"
+#include "hardware/esp32s2_pinmap.h"
+#include "hardware/esp32s2_spi.h"
+#include "hardware/esp32s2_soc.h"
+#include "hardware/esp32s2_system.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Check if Chip-Select pin will be controlled via software */
+
+#ifdef CONFIG_ESP32S2_SPI_SWCS
+#  define SPI_HAVE_SWCS 1
+#else
+#  define SPI_HAVE_SWCS 0
+#endif
+
+/* SPI default frequency (limited by clock divider) */
+
+#define SPI_DEFAULT_FREQ  (400000)
+
+/* SPI default width */
+
+#define SPI_DEFAULT_WIDTH (8)
+
+/* SPI default mode */
+
+#define SPI_DEFAULT_MODE  (SPIDEV_MODE0)
+
+/* Helper for applying the mask for a given register field.
+ * Mask is determined by the macros suffixed with _V and _S from the
+ * peripheral register description.
+ */
+
+#define VALUE_MASK(_val, _field) (((_val) & (_field##_V)) << (_field##_S))
+
+/* SPI Maximum buffer size in bytes */
+
+#define SPI_MAX_BUF_SIZE (64)
+
+#ifndef MIN
+#  define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* SPI Device hardware configuration */
+
+struct esp32s2_spi_config_s
+{
+  uint32_t clk_freq;          /* SPI default clock frequency */
+  uint32_t width;             /* SPI default width */
+  enum spi_mode_e mode;       /* SPI default mode */
+
+  uint8_t id;                 /* ESP32-S2 SPI device ID: SPIx {2,3} */
+  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 clk_bit;           /* Clock enable bit */
+  uint32_t rst_bit;           /* SPI reset bit */
+  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 */
+};
+
+struct esp32s2_spi_priv_s
+{
+  /* Externally visible part of the SPI interface */
+
+  struct spi_dev_s spi_dev;
+
+  /* Port configuration */
+
+  const struct esp32s2_spi_config_s *config;
+  int refs;             /* Reference count */
+  sem_t exclsem;        /* Held while chip is selected for mutual exclusion */
+  uint32_t frequency;   /* Requested clock frequency */
+  uint32_t actual;      /* Actual clock frequency */
+  enum spi_mode_e mode; /* Actual SPI hardware mode */
+  uint8_t nbits;        /* Actual SPI send/receive bits once transmission */
+  spinlock_t lock;      /* Device specific lock. */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int esp32s2_spi_lock(struct spi_dev_s *dev, bool lock);
+#ifndef CONFIG_ESP32S2_SPI_UDCS
+static void esp32s2_spi_select(struct spi_dev_s *dev,
+                               uint32_t devid, bool selected);
+#endif
+static uint32_t esp32s2_spi_setfrequency(struct spi_dev_s *dev,
+                                         uint32_t frequency);
+static void esp32s2_spi_setmode(struct spi_dev_s *dev,
+                                enum spi_mode_e mode);
+static void esp32s2_spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int esp32s2_spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t esp32s2_spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void esp32s2_spi_exchange(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 void *rxbuffer, size_t nwords);
+static void esp32s2_spi_poll_exchange(struct esp32s2_spi_priv_s *priv,
+                                      const void *txbuffer,
+                                      void *rxbuffer,
+                                      size_t nwords);
+#ifndef CONFIG_SPI_EXCHANGE
+static void esp32s2_spi_sndblock(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 size_t nwords);
+static void esp32s2_spi_recvblock(struct spi_dev_s *dev,
+                                  void *rxbuffer,
+                                  size_t nwords);
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+static int esp32s2_spi_trigger(struct spi_dev_s *dev);
+#endif
+static void esp32s2_spi_init(struct spi_dev_s *dev);
+static void esp32s2_spi_deinit(struct spi_dev_s *dev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP32S2_SPI2
+static const struct esp32s2_spi_config_s esp32s2_spi2_config =
+{
+  .clk_freq     = SPI_DEFAULT_FREQ,
+  .width        = SPI_DEFAULT_WIDTH,
+  .id           = 2,
+  .mode         = SPI_DEFAULT_MODE,
+  .cs_pin       = CONFIG_ESP32S2_SPI2_CSPIN,
+  .mosi_pin     = CONFIG_ESP32S2_SPI2_MOSIPIN,
+  .miso_pin     = CONFIG_ESP32S2_SPI2_MISOPIN,
+  .clk_pin      = CONFIG_ESP32S2_SPI2_CLKPIN,
+  .clk_bit      = SYSTEM_SPI2_CLK_EN,
+  .rst_bit      = SYSTEM_SPI2_RST,
+  .cs_insig     = FSPICS0_IN_IDX,
+  .cs_outsig    = FSPICS0_OUT_IDX,
+  .mosi_insig   = FSPID_IN_IDX,
+  .mosi_outsig  = FSPID_OUT_IDX,
+  .miso_insig   = FSPIQ_IN_IDX,
+  .miso_outsig  = FSPIQ_OUT_IDX,
+  .clk_insig    = FSPICLK_IN_IDX,
+  .clk_outsig   = FSPICLK_OUT_IDX
+};
+
+static const struct spi_ops_s esp32s2_spi2_ops =
+{
+  .lock              = esp32s2_spi_lock,
+#ifdef CONFIG_ESP32S2_SPI_UDCS
+  .select            = esp32s2_spi2_select,
+#else
+  .select            = esp32s2_spi_select,
+#endif
+  .setfrequency      = esp32s2_spi_setfrequency,
+  .setmode           = esp32s2_spi_setmode,
+  .setbits           = esp32s2_spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = esp32s2_spi_hwfeatures,
+#endif
+  .status            = esp32s2_spi2_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = esp32s2_spi2_cmddata,
+#endif
+  .send              = esp32s2_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = esp32s2_spi_exchange,
+#else
+  .sndblock          = esp32s2_spi_sndblock,
+  .recvblock         = esp32s2_spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = esp32s2_spi_trigger,
+#endif
+  .registercallback  = NULL,
+};
+
+static struct esp32s2_spi_priv_s esp32s2_spi2_priv =
+{
+  .spi_dev     =
+                {
+                  .ops = &esp32s2_spi2_ops
+                },
+  .config      = &esp32s2_spi2_config,
+  .refs        = 0,
+  .exclsem     = SEM_INITIALIZER(0),
+  .frequency   = 0,
+  .actual      = 0,
+  .mode        = 0,
+  .nbits       = 0
+};
+#endif /* CONFIG_ESP32S2_SPI2 */
+
+#ifdef CONFIG_ESP32S2_SPI3
+static const struct esp32s2_spi_config_s esp32s2_spi3_config =
+{
+  .clk_freq     = SPI_DEFAULT_FREQ,
+  .width        = SPI_DEFAULT_WIDTH,
+  .id           = 3,
+  .mode         = SPI_DEFAULT_MODE,
+  .cs_pin       = CONFIG_ESP32S2_SPI3_CSPIN,
+  .mosi_pin     = CONFIG_ESP32S2_SPI3_MOSIPIN,
+  .miso_pin     = CONFIG_ESP32S2_SPI3_MISOPIN,
+  .clk_pin      = CONFIG_ESP32S2_SPI3_CLKPIN,
+  .clk_bit      = SYSTEM_SPI3_CLK_EN,
+  .rst_bit      = SYSTEM_SPI3_RST,
+  .cs_insig     = FSPICS0_IN_IDX,
+  .cs_outsig    = FSPICS0_OUT_IDX,
+  .mosi_insig   = FSPID_IN_IDX,
+  .mosi_outsig  = FSPID_OUT_IDX,
+  .miso_insig   = FSPIQ_IN_IDX,
+  .miso_outsig  = FSPIQ_OUT_IDX,
+  .clk_insig    = FSPICLK_IN_IDX,
+  .clk_outsig   = FSPICLK_OUT_IDX
+};
+
+static const struct spi_ops_s esp32s2_spi3_ops =
+{
+  .lock              = esp32s2_spi_lock,
+#ifdef CONFIG_ESP32S2_SPI_UDCS
+  .select            = esp32s2_spi3_select,
+#else
+  .select            = esp32s2_spi_select,
+#endif
+  .setfrequency      = esp32s2_spi_setfrequency,
+  .setmode           = esp32s2_spi_setmode,
+  .setbits           = esp32s2_spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = esp32s2_spi_hwfeatures,
+#endif
+  .status            = esp32s2_spi3_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = esp32s2_spi3_cmddata,
+#endif
+  .send              = esp32s2_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = esp32s2_spi_exchange,
+#else
+  .sndblock          = esp32s2_spi_sndblock,
+  .recvblock         = esp32s2_spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = esp32s2_spi_trigger,
+#endif
+  .registercallback  = NULL,
+};
+
+static struct esp32s2_spi_priv_s esp32s2_spi3_priv =
+{
+  .spi_dev     =
+                {
+                  .ops = &esp32s2_spi3_ops
+                },
+  .config      = &esp32s2_spi3_config,
+  .refs        = 0,
+  .exclsem     = SEM_INITIALIZER(0),
+  .frequency   = 0,
+  .actual      = 0,
+  .mode        = 0,
+  .nbits       = 0
+};
+#endif /* CONFIG_ESP32S2_SPI3 */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32s2_spi_set_regbits
+ *
+ * Description:
+ *   Set the bits of the SPI register.
+ *
+ * Input Parameters:
+ *   addr   - Address of the register of interest
+ *   bits   - Bits to be set
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static inline void esp32s2_spi_set_regbits(uint32_t addr, uint32_t bits)
+{
+  uint32_t tmp = getreg32(addr);
+
+  putreg32(tmp | bits, addr);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_clr_regbits
+ *
+ * Description:
+ *   Clear the bits of the SPI register.
+ *
+ * Input Parameters:
+ *   addr   - Address of the register of interest
+ *   bits   - Bits to be cleared
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static inline void esp32s2_spi_clr_regbits(uint32_t addr, uint32_t bits)
+{
+  uint32_t tmp = getreg32(addr);
+
+  putreg32(tmp & ~bits, addr);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_iomux
+ *
+ * Description:
+ *   Check if the option SPI GPIO pins can use IOMUX directly
+ *
+ * Input Parameters:
+ *   priv   - Private SPI device structure
+ *
+ * Returned Value:
+ *   True if can use IOMUX or false if can't.
+ *
+ ****************************************************************************/
+
+static inline bool esp32s2_spi_iomux(struct esp32s2_spi_priv_s *priv)
+{
+  bool mapped = false;
+  const struct esp32s2_spi_config_s *cfg = priv->config;
+
+  /* We only need to check SPI2, SPI3 doesn't support IOMUX */
+
+  if (cfg->id == 2)
+    {
+      if (cfg->mosi_pin == SPI2_IOMUX_MOSIPIN &&
+#ifndef CONFIG_ESP32S2_SPI_SWCS
+          cfg->cs_pin == SPI2_IOMUX_CSPIN &&
+#endif
+          cfg->miso_pin == SPI2_IOMUX_MISOPIN &&
+          cfg->clk_pin == SPI2_IOMUX_CLKPIN)
+        {
+          mapped = true;
+        }
+    }
+
+  return mapped;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_lock
+ *
+ * Description:
+ *   Lock or unlock the SPI device.
+ *
+ * Input Parameters:
+ *   dev    - Device-specific state data
+ *   lock   - true: Lock SPI bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   The result of lock or unlock the SPI device.
+ *
+ ****************************************************************************/
+
+static int esp32s2_spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  int ret;
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_select
+ *
+ * Description:
+ *   Enable/disable the SPI chip select. The implementation of this method
+ *   must include handshaking: If a device is selected, it must hold off
+ *   all other attempts to select the device until the device is deselected.
+ *
+ *   If ESP32S2_SPI_SWCS is disabled, the driver will use hardware CS so that
+ *   once transmission is started the hardware selects the device and when
+ *   this transmission is done hardware deselects the device automatically.
+ *   So, this function will do nothing.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   devid    - Identifies the device to select
+ *   selected - true: slave selected, false: slave de-selected
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_ESP32S2_SPI_UDCS
+static void esp32s2_spi_select(struct spi_dev_s *dev,
+                               uint32_t devid, bool selected)
+{
+#if SPI_HAVE_SWCS
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  esp32s2_gpiowrite(priv->config->cs_pin, !selected);
+#endif
+
+  spiinfo("devid: %08" PRIx32 " CS: %s\n",
+          devid, selected ? "select" : "free");
+}
+#endif
+
+/****************************************************************************
+ * Name: esp32s2_spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev       - Device-specific state data
+ *   frequency - The requested SPI frequency
+ *
+ * Returned Value:
+ *   Returns the current selected frequency.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_setfrequency(struct spi_dev_s *dev,
+                                         uint32_t frequency)
+{
+  uint32_t reg_val;
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+  const uint32_t duty_cycle = 128;
+
+  if (priv->frequency == frequency)
+    {
+      /* Requested frequency is the same as the current frequency. */
+
+      return priv->actual;
+    }
+
+  /* In HW, n, h and l fields range from 1 to 64, pre ranges from 1 to 8K.
+   * The value written to register is one lower than the used value.
+   */
+
+  if (frequency > ((APB_CLK_FREQ / 4) * 3))
+    {
+      /* Using APB frequency directly will give us the best result here. */
+
+      reg_val = SPI_CLK_EQU_SYSCLK_M;
+      priv->actual = APB_CLK_FREQ;
+    }
+  else
+    {
+      /* For best duty cycle resolution, we want n to be as close to 32 as
+       * possible, but we also need a pre/n combo that gets us as close as
+       * possible to the intended frequency. To do this, we bruteforce n and
+       * calculate the best pre to go along with that. If there's a choice
+       * between pre/n combos that give the same result, use the one with the
+       * higher n.
+       */
+
+      int32_t pre;
+      int32_t n;
+      int32_t h;
+      int32_t l;
+      int32_t bestn = -1;
+      int32_t bestpre = -1;
+      int32_t besterr = 0;
+      int32_t errval;
+
+      /* Start at n = 2. We need to be able to set h/l so we have at least
+       * one high and one low pulse.
+       */
+
+      for (n = 2; n <= 64; n++)
+        {
+          /* Effectively, this does:
+           *   pre = round((APB_CLK_FREQ / n) / frequency)
+           */
+
+          pre = ((APB_CLK_FREQ / n) + (frequency / 2)) / frequency;
+
+          if (pre <= 0)
+            {
+              pre = 1;
+            }
+
+          if (pre > 16)
+            {
+              pre = 16;
+            }
+
+          errval = abs(APB_CLK_FREQ / (pre * n) - frequency);
+          if (bestn == -1 || errval <= besterr)
+            {
+              besterr = errval;
+              bestn = n;
+              bestpre = pre;
+            }
+        }
+
+      n = bestn;
+      pre = bestpre;
+      l = n;
+
+      /* Effectively, this does:
+       *   h = round((duty_cycle * n) / 256)
+       */
+
+      h = (duty_cycle * n + 127) / 256;
+      if (h <= 0)
+        {
+          h = 1;
+        }
+
+      reg_val = ((l - 1) << SPI_CLKCNT_L_S) |
+                ((h - 1) << SPI_CLKCNT_H_S) |
+                ((n - 1) << SPI_CLKCNT_N_S) |
+                ((pre - 1) << SPI_CLKDIV_PRE_S);
+
+      priv->actual = APB_CLK_FREQ / (n * pre);
+    }
+
+  priv->frequency = frequency;
+
+  putreg32(reg_val, SPI_CLOCK_REG(priv->config->id));
+
+  spiinfo("frequency=%" PRIu32 ", actual=%" PRIu32 "\n",
+          priv->frequency, priv->actual);
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The requested SPI mode
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_setmode(struct spi_dev_s *dev,
+                                enum spi_mode_e mode)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      uint32_t ck_idle_edge;
+      uint32_t ck_out_edge;
+
+      switch (mode)
+        {
+          case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+            ck_idle_edge = 0;
+            ck_out_edge = 0;
+            break;
+
+          case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+            ck_idle_edge = 0;
+            ck_out_edge = 1;
+            break;
+
+          case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+            ck_idle_edge = 1;
+            ck_out_edge = 1;
+            break;
+
+          case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+            ck_idle_edge = 1;
+            ck_out_edge = 0;
+            break;
+
+          default:
+            spierr("Invalid mode: %d\n", mode);
+            DEBUGPANIC();
+            return;
+        }
+
+      esp32s2_spi_clr_regbits(SPI_MISC_REG(priv->config->id),
+                              SPI_CK_IDLE_EDGE_M);
+      esp32s2_spi_set_regbits(SPI_MISC_REG(priv->config->id),
+                              VALUE_MASK(ck_idle_edge, SPI_CK_IDLE_EDGE));
+
+      esp32s2_spi_clr_regbits(SPI_USER_REG(priv->config->id),
+                              SPI_CK_OUT_EDGE_M);
+      esp32s2_spi_set_regbits(SPI_USER_REG(priv->config->id),
+                              VALUE_MASK(ck_out_edge, SPI_CK_OUT_EDGE));
+
+      priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_setbits
+ *
+ * Description:
+ *   Set the number of bits per word.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *   nbits - The number of bits in an SPI word.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  spiinfo("nbits=%d\n", nbits);
+
+  priv->nbits = nbits;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_hwfeatures
+ *
+ * Description:
+ *   Set hardware-specific feature flags.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   features - H/W feature flags
+ *
+ * Returned Value:
+ *   Zero (OK) if the selected H/W features are enabled; A negated errno
+ *   value if any H/W feature is not supportable.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_HWFEATURES
+static int esp32s2_spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features)
+{
+  /* Other H/W features are not supported */
+
+  return (features == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp32s2_spi_poll_send
+ *
+ * Description:
+ *   Send one word on SPI by polling mode.
+ *
+ * Input Parameters:
+ *   priv - SPI private state data
+ *   wd   - The word to send. The size of the data is determined by the
+ *          number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   Received value.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_poll_send(struct esp32s2_spi_priv_s *priv,
+                                      uint32_t wd)
+{
+  uint32_t val;
+
+  const uintptr_t spi_miso_dlen_reg = SPI_MISO_DLEN_REG(priv->config->id);
+  const uintptr_t spi_mosi_dlen_reg = SPI_MOSI_DLEN_REG(priv->config->id);
+  const uintptr_t spi_w0_reg = SPI_W0_REG(priv->config->id);
+  const uintptr_t spi_cmd_reg = SPI_CMD_REG(priv->config->id);
+
+  putreg32((priv->nbits - 1), spi_miso_dlen_reg);
+  putreg32((priv->nbits - 1), spi_mosi_dlen_reg);
+
+  putreg32(wd, spi_w0_reg);
+
+  esp32s2_spi_set_regbits(spi_cmd_reg, SPI_USR_M);
+
+  while ((getreg32(spi_cmd_reg) & SPI_USR_M) != 0)
+    {
+      ;
+    }
+
+  val = getreg32(spi_w0_reg);
+
+  spiinfo("send=0x%" PRIx32 " and recv=0x%" PRIx32 "\n", wd, val);
+
+  return val;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_send
+ *
+ * Description:
+ *   Send one word on SPI.
+ *
+ * Input Parameters:
+ *   dev - Device-specific state data
+ *   wd  - The word to send. The size of the data is determined by the
+ *         number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   Received value.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  return esp32s2_spi_poll_send(priv, wd);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_poll_exchange
+ *
+ * Description:
+ *   Exchange a block of data from SPI.
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_poll_exchange(struct esp32s2_spi_priv_s *priv,
+                                      const void *txbuffer,
+                                      void *rxbuffer,
+                                      size_t nwords)
+{
+  const uint32_t total_bytes = nwords * (priv->nbits / 8);
+  uintptr_t bytes_remaining = total_bytes;
+  const uint8_t *tp = (const uint8_t *)txbuffer;
+  uint8_t *rp = (uint8_t *)rxbuffer;
+
+  while (bytes_remaining != 0)
+    {
+      /* Initialize data_buf_reg with the address of the first data buffer
+       * register (W0).
+       */
+
+      uintptr_t data_buf_reg = SPI_W0_REG(priv->config->id);
+      uint32_t transfer_size = MIN(SPI_MAX_BUF_SIZE, bytes_remaining);
+
+      /* Write data words to data buffer registers.
+       * SPI peripheral contains 16 registers (W0 - W15).
+       */
+
+      for (int i = 0 ; i < transfer_size; i += sizeof(uintptr_t))
+        {
+          uint32_t w_wd = UINT32_MAX;
+
+          if (tp != NULL)
+            {
+              memcpy(&w_wd, tp, sizeof(uintptr_t));
+
+              tp += sizeof(uintptr_t);
+            }
+
+          putreg32(w_wd, data_buf_reg);
+
+          spiinfo("send=0x%" PRIx32 " data_reg=0x%" PRIxPTR "\n",
+                  w_wd, data_buf_reg);
+
+          /* Update data_buf_reg to point to the next data buffer register. */
+
+          data_buf_reg += sizeof(uintptr_t);
+        }
+
+      esp32s2_spi_set_regbits(SPI_USER_REG(priv->config->id),
+                              SPI_USR_MOSI_M);
+
+      if (rp == NULL)
+        {
+          esp32s2_spi_clr_regbits(SPI_USER_REG(priv->config->id),
+                                  SPI_USR_MISO_M);
+        }
+      else
+        {
+          esp32s2_spi_set_regbits(SPI_USER_REG(priv->config->id),
+                                  SPI_USR_MISO_M);
+        }
+
+      putreg32((transfer_size * 8) - 1,
+               SPI_MOSI_DLEN_REG(priv->config->id));
+
+      putreg32((transfer_size * 8) - 1,
+               SPI_MISO_DLEN_REG(priv->config->id));
+
+      /* Trigger start of user-defined transaction for master. */
+
+      esp32s2_spi_set_regbits(SPI_CMD_REG(priv->config->id), SPI_USR_M);
+
+      /* Wait for the user-defined transaction to finish. */
+
+      while ((getreg32(SPI_CMD_REG(priv->config->id)) & SPI_USR_M) != 0)
+        {
+          ;
+        }
+
+      if (rp != NULL)
+        {
+          /* Set data_buf_reg with the address of the first data buffer
+           * register (W0).
+           */
+
+          data_buf_reg = SPI_W0_REG(priv->config->id);
+
+          /* Read received data words from SPI data buffer registers. */
+
+          for (int i = 0 ; i < transfer_size; i += sizeof(uintptr_t))
+            {
+              uint32_t r_wd = getreg32(data_buf_reg);
+
+              spiinfo("recv=0x%" PRIx32 " data_reg=0x%" PRIxPTR "\n",
+                      r_wd, data_buf_reg);
+
+              memcpy(rp, &r_wd, sizeof(uintptr_t));
+
+              rp += sizeof(uintptr_t);
+
+              /* Update data_buf_reg to point to the next data buffer
+               * register.
+               */
+
+              data_buf_reg += sizeof(uintptr_t);
+            }
+        }
+
+      bytes_remaining -= transfer_size;
+    }
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_exchange
+ *
+ * Description:
+ *   Exchange a block of data from SPI.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific 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.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_exchange(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 void *rxbuffer,
+                                 size_t nwords)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  esp32s2_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords);
+}
+
+#ifndef CONFIG_SPI_EXCHANGE
+
+/****************************************************************************
+ * Name: esp32s2_spi_sndblock
+ *
+ * Description:
+ *   Send a block of data on SPI.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   nwords   - The length of data to send from the buffer in number 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.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_sndblock(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 size_t nwords)
+{
+  spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords);
+
+  esp32s2_spi_exchange(dev, txbuffer, NULL, nwords);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_recvblock
+ *
+ * Description:
+ *   Receive a block of data from SPI.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   rxbuffer - A pointer to the buffer in which to receive data
+ *   nwords   - The length of data that can be received in the buffer in
+ *              number 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.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_recvblock(struct spi_dev_s *dev,
+                                  void *rxbuffer,
+                                  size_t nwords)
+{
+  spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords);
+
+  esp32s2_spi_exchange(dev, NULL, rxbuffer, nwords);
+}
+#endif
+
+/****************************************************************************
+ * Name: esp32s2_spi_init
+ *
+ * Description:
+ *   Initialize ESP32-S2 SPI hardware interface.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_init(struct spi_dev_s *dev)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+  const struct esp32s2_spi_config_s *config = priv->config;
+  uint32_t regval;
+
+  /* Initialize the SPI semaphore that enforces mutually exclusive access */
+
+  nxsem_init(&priv->exclsem, 0, 1);
+
+  esp32s2_gpiowrite(config->cs_pin, true);
+  esp32s2_gpiowrite(config->mosi_pin, true);
+  esp32s2_gpiowrite(config->miso_pin, true);
+  esp32s2_gpiowrite(config->clk_pin, true);
+
+#if SPI_HAVE_SWCS
+  esp32s2_configgpio(config->cs_pin, OUTPUT_FUNCTION_1);
+  esp32s2_gpio_matrix_out(config->cs_pin, SIG_GPIO_OUT_IDX, 0, 0);
+#endif
+
+  /* SPI3 doesn't have IOMUX, if SPI3 is enabled use GPIO Matrix for both */
+
+  if (esp32s2_spi_iomux(priv))
+    {
+#if !SPI_HAVE_SWCS
+      esp32s2_configgpio(config->cs_pin, OUTPUT_FUNCTION_5);
+      esp32s2_gpio_matrix_out(config->cs_pin, SIG_GPIO_OUT_IDX, 0, 0);
+#endif
+      esp32s2_configgpio(config->mosi_pin, OUTPUT_FUNCTION_5);
+      esp32s2_gpio_matrix_out(config->mosi_pin, SIG_GPIO_OUT_IDX, 0, 0);
+
+      esp32s2_configgpio(config->miso_pin, INPUT_FUNCTION_5 | PULLUP);
+      esp32s2_gpio_matrix_out(config->miso_pin, SIG_GPIO_OUT_IDX, 0, 0);
+
+      esp32s2_configgpio(config->clk_pin, OUTPUT_FUNCTION_5);
+      esp32s2_gpio_matrix_out(config->clk_pin, SIG_GPIO_OUT_IDX, 0, 0);
+    }
+  else
+    {
+#if !SPI_HAVE_SWCS
+      esp32s2_configgpio(config->cs_pin, OUTPUT);
+      esp32s2_gpio_matrix_out(config->cs_pin, config->cs_outsig, 0, 0);
+#endif
+      esp32s2_configgpio(config->mosi_pin, OUTPUT);
+      esp32s2_gpio_matrix_out(config->mosi_pin, config->mosi_outsig, 0, 0);
+
+      esp32s2_configgpio(config->miso_pin, INPUT | PULLUP);
+      esp32s2_gpio_matrix_in(config->miso_pin, config->miso_insig, 0);
+
+      esp32s2_configgpio(config->clk_pin, OUTPUT);
+      esp32s2_gpio_matrix_out(config->clk_pin, config->clk_outsig, 0, 0);
+    }
+
+  modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, 0, config->clk_bit);
+  modifyreg32(SYSTEM_PERIP_RST_EN0_REG, config->rst_bit, 0);
+
+  regval = SPI_DOUTDIN_M | SPI_USR_MISO_M | SPI_USR_MOSI_M | SPI_CS_HOLD_M;
+  putreg32(regval, SPI_USER_REG(priv->config->id));
+  putreg32(0, SPI_USER1_REG(priv->config->id));
+  putreg32(0, SPI_SLAVE_REG(priv->config->id));
+  putreg32(SPI_CS1_DIS_M | SPI_CS2_DIS_M,
+           SPI_MISC_REG(priv->config->id));
+
+#if SPI_HAVE_SWCS
+  esp32s2_spi_set_regbits(SPI_MISC_REG(priv->config->id), SPI_CS0_DIS_M);
+#endif
+
+  putreg32(0, SPI_CTRL_REG(priv->config->id));
+  putreg32(VALUE_MASK(0, SPI_CS_HOLD_TIME),
+           SPI_USER1_REG(priv->config->id));
+
+  esp32s2_spi_setfrequency(dev, config->clk_freq);
+  esp32s2_spi_setbits(dev, config->width);
+  esp32s2_spi_setmode(dev, config->mode);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_deinit
+ *
+ * Description:
+ *   Deinitialize ESP32-S2 SPI hardware interface.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_deinit(struct spi_dev_s *dev)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  modifyreg32(SYSTEM_PERIP_RST_EN0_REG, 0, priv->config->clk_bit);

Review Comment:
   ```suggestion
     modifyreg32(SYSTEM_PERIP_RST_EN0_REG, 0, priv->config->rst_bit);
   ```
   This is also wrong on the `esp32`, `esp32c3` and `esp32s3` chips, could you take the opportunity to fix them as well?
   Luckily both `clk_bit` and `rst_bit` refer to `BIT(6)` from their respective registers, so there won't be any real impact.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] pkarashchenko merged pull request #6823: xtensa/esp32s2: Add basic support to SPI

Posted by GitBox <gi...@apache.org>.
pkarashchenko merged PR #6823:
URL: https://github.com/apache/incubator-nuttx/pull/6823


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] pkarashchenko commented on a diff in pull request #6823: xtensa/esp32s2: Add basic support to SPI

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on code in PR #6823:
URL: https://github.com/apache/incubator-nuttx/pull/6823#discussion_r942075903


##########
arch/xtensa/src/esp32s2/esp32s2_spi.c:
##########
@@ -0,0 +1,1231 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32s2/esp32s2_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_ESP32S2_SPI
+
+#include <assert.h>
+#include <debug.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/spi/spi.h>
+
+#include <arch/board/board.h>
+
+#include "esp32s2_spi.h"
+#include "esp32s2_irq.h"
+#include "esp32s2_gpio.h"
+
+#include "xtensa.h"
+#include "hardware/esp32s2_gpio_sigmap.h"
+#include "hardware/esp32s2_pinmap.h"
+#include "hardware/esp32s2_spi.h"
+#include "hardware/esp32s2_soc.h"
+#include "hardware/esp32s2_system.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Check if Chip-Select pin will be controlled via software */
+
+#ifdef CONFIG_ESP32S2_SPI_SWCS
+#  define SPI_HAVE_SWCS 1
+#else
+#  define SPI_HAVE_SWCS 0
+#endif
+
+/* SPI default frequency (limited by clock divider) */
+
+#define SPI_DEFAULT_FREQ  (400000)
+
+/* SPI default width */
+
+#define SPI_DEFAULT_WIDTH (8)
+
+/* SPI default mode */
+
+#define SPI_DEFAULT_MODE  (SPIDEV_MODE0)
+
+/* Helper for applying the mask for a given register field.
+ * Mask is determined by the macros suffixed with _V and _S from the
+ * peripheral register description.
+ */
+
+#define VALUE_MASK(_val, _field) (((_val) & (_field##_V)) << (_field##_S))
+
+/* SPI Maximum buffer size in bytes */
+
+#define SPI_MAX_BUF_SIZE (64)
+
+#ifndef MIN
+#  define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* SPI Device hardware configuration */
+
+struct esp32s2_spi_config_s
+{
+  uint32_t clk_freq;          /* SPI default clock frequency */
+  uint32_t width;             /* SPI default width */
+  enum spi_mode_e mode;       /* SPI default mode */
+
+  uint8_t id;                 /* ESP32-S2 SPI device ID: SPIx {2,3} */
+  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 clk_bit;           /* Clock enable bit */
+  uint32_t rst_bit;           /* SPI reset bit */
+  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 */
+};
+
+struct esp32s2_spi_priv_s
+{
+  /* Externally visible part of the SPI interface */
+
+  struct spi_dev_s spi_dev;
+
+  /* Port configuration */
+
+  const struct esp32s2_spi_config_s *config;
+  int refs;             /* Reference count */
+  sem_t exclsem;        /* Held while chip is selected for mutual exclusion */
+  uint32_t frequency;   /* Requested clock frequency */
+  uint32_t actual;      /* Actual clock frequency */
+  enum spi_mode_e mode; /* Actual SPI hardware mode */
+  uint8_t nbits;        /* Actual SPI send/receive bits once transmission */
+  spinlock_t lock;      /* Device specific lock. */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int esp32s2_spi_lock(struct spi_dev_s *dev, bool lock);
+#ifndef CONFIG_ESP32S2_SPI_UDCS
+static void esp32s2_spi_select(struct spi_dev_s *dev,
+                               uint32_t devid, bool selected);
+#endif
+static uint32_t esp32s2_spi_setfrequency(struct spi_dev_s *dev,
+                                         uint32_t frequency);
+static void esp32s2_spi_setmode(struct spi_dev_s *dev,
+                                enum spi_mode_e mode);
+static void esp32s2_spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int esp32s2_spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t esp32s2_spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void esp32s2_spi_exchange(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 void *rxbuffer, size_t nwords);
+static void esp32s2_spi_poll_exchange(struct esp32s2_spi_priv_s *priv,
+                                      const void *txbuffer,
+                                      void *rxbuffer,
+                                      size_t nwords);
+#ifndef CONFIG_SPI_EXCHANGE
+static void esp32s2_spi_sndblock(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 size_t nwords);
+static void esp32s2_spi_recvblock(struct spi_dev_s *dev,
+                                  void *rxbuffer,
+                                  size_t nwords);
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+static int esp32s2_spi_trigger(struct spi_dev_s *dev);
+#endif
+static void esp32s2_spi_init(struct spi_dev_s *dev);
+static void esp32s2_spi_deinit(struct spi_dev_s *dev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP32S2_SPI2
+static const struct esp32s2_spi_config_s esp32s2_spi2_config =
+{
+  .clk_freq     = SPI_DEFAULT_FREQ,
+  .width        = SPI_DEFAULT_WIDTH,
+  .id           = 2,
+  .mode         = SPI_DEFAULT_MODE,
+  .cs_pin       = CONFIG_ESP32S2_SPI2_CSPIN,
+  .mosi_pin     = CONFIG_ESP32S2_SPI2_MOSIPIN,
+  .miso_pin     = CONFIG_ESP32S2_SPI2_MISOPIN,
+  .clk_pin      = CONFIG_ESP32S2_SPI2_CLKPIN,
+  .clk_bit      = SYSTEM_SPI2_CLK_EN,
+  .rst_bit      = SYSTEM_SPI2_RST,
+  .cs_insig     = FSPICS0_IN_IDX,
+  .cs_outsig    = FSPICS0_OUT_IDX,
+  .mosi_insig   = FSPID_IN_IDX,
+  .mosi_outsig  = FSPID_OUT_IDX,
+  .miso_insig   = FSPIQ_IN_IDX,
+  .miso_outsig  = FSPIQ_OUT_IDX,
+  .clk_insig    = FSPICLK_IN_IDX,
+  .clk_outsig   = FSPICLK_OUT_IDX
+};
+
+static const struct spi_ops_s esp32s2_spi2_ops =
+{
+  .lock              = esp32s2_spi_lock,
+#ifdef CONFIG_ESP32S2_SPI_UDCS
+  .select            = esp32s2_spi2_select,
+#else
+  .select            = esp32s2_spi_select,
+#endif
+  .setfrequency      = esp32s2_spi_setfrequency,
+  .setmode           = esp32s2_spi_setmode,
+  .setbits           = esp32s2_spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = esp32s2_spi_hwfeatures,
+#endif
+  .status            = esp32s2_spi2_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = esp32s2_spi2_cmddata,
+#endif
+  .send              = esp32s2_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = esp32s2_spi_exchange,
+#else
+  .sndblock          = esp32s2_spi_sndblock,
+  .recvblock         = esp32s2_spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = esp32s2_spi_trigger,
+#endif
+  .registercallback  = NULL,
+};
+
+static struct esp32s2_spi_priv_s esp32s2_spi2_priv =
+{
+  .spi_dev     =
+                {
+                  .ops = &esp32s2_spi2_ops
+                },
+  .config      = &esp32s2_spi2_config,
+  .refs        = 0,
+  .exclsem     = SEM_INITIALIZER(0),
+  .frequency   = 0,
+  .actual      = 0,
+  .mode        = 0,
+  .nbits       = 0
+};
+#endif /* CONFIG_ESP32S2_SPI2 */
+
+#ifdef CONFIG_ESP32S2_SPI3
+static const struct esp32s2_spi_config_s esp32s2_spi3_config =
+{
+  .clk_freq     = SPI_DEFAULT_FREQ,
+  .width        = SPI_DEFAULT_WIDTH,
+  .id           = 3,
+  .mode         = SPI_DEFAULT_MODE,
+  .cs_pin       = CONFIG_ESP32S2_SPI3_CSPIN,
+  .mosi_pin     = CONFIG_ESP32S2_SPI3_MOSIPIN,
+  .miso_pin     = CONFIG_ESP32S2_SPI3_MISOPIN,
+  .clk_pin      = CONFIG_ESP32S2_SPI3_CLKPIN,
+  .clk_bit      = SYSTEM_SPI3_CLK_EN,
+  .rst_bit      = SYSTEM_SPI3_RST,
+  .cs_insig     = FSPICS0_IN_IDX,
+  .cs_outsig    = FSPICS0_OUT_IDX,
+  .mosi_insig   = FSPID_IN_IDX,
+  .mosi_outsig  = FSPID_OUT_IDX,
+  .miso_insig   = FSPIQ_IN_IDX,
+  .miso_outsig  = FSPIQ_OUT_IDX,
+  .clk_insig    = FSPICLK_IN_IDX,
+  .clk_outsig   = FSPICLK_OUT_IDX
+};
+
+static const struct spi_ops_s esp32s2_spi3_ops =
+{
+  .lock              = esp32s2_spi_lock,
+#ifdef CONFIG_ESP32S2_SPI_UDCS
+  .select            = esp32s2_spi3_select,
+#else
+  .select            = esp32s2_spi_select,
+#endif
+  .setfrequency      = esp32s2_spi_setfrequency,
+  .setmode           = esp32s2_spi_setmode,
+  .setbits           = esp32s2_spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = esp32s2_spi_hwfeatures,
+#endif
+  .status            = esp32s2_spi3_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = esp32s2_spi3_cmddata,
+#endif
+  .send              = esp32s2_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = esp32s2_spi_exchange,
+#else
+  .sndblock          = esp32s2_spi_sndblock,
+  .recvblock         = esp32s2_spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = esp32s2_spi_trigger,
+#endif
+  .registercallback  = NULL,
+};
+
+static struct esp32s2_spi_priv_s esp32s2_spi3_priv =
+{
+  .spi_dev     =
+                {
+                  .ops = &esp32s2_spi3_ops
+                },

Review Comment:
   ```suggestion
     .spi_dev     =
       {
         .ops = &esp32s2_spi3_ops
       },
   ```



##########
arch/xtensa/src/esp32s2/esp32s2_spi.c:
##########
@@ -0,0 +1,1231 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32s2/esp32s2_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_ESP32S2_SPI
+
+#include <assert.h>
+#include <debug.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/spi/spi.h>
+
+#include <arch/board/board.h>
+
+#include "esp32s2_spi.h"
+#include "esp32s2_irq.h"
+#include "esp32s2_gpio.h"
+
+#include "xtensa.h"
+#include "hardware/esp32s2_gpio_sigmap.h"
+#include "hardware/esp32s2_pinmap.h"
+#include "hardware/esp32s2_spi.h"
+#include "hardware/esp32s2_soc.h"
+#include "hardware/esp32s2_system.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Check if Chip-Select pin will be controlled via software */
+
+#ifdef CONFIG_ESP32S2_SPI_SWCS
+#  define SPI_HAVE_SWCS 1
+#else
+#  define SPI_HAVE_SWCS 0
+#endif
+
+/* SPI default frequency (limited by clock divider) */
+
+#define SPI_DEFAULT_FREQ  (400000)
+
+/* SPI default width */
+
+#define SPI_DEFAULT_WIDTH (8)
+
+/* SPI default mode */
+
+#define SPI_DEFAULT_MODE  (SPIDEV_MODE0)
+
+/* Helper for applying the mask for a given register field.
+ * Mask is determined by the macros suffixed with _V and _S from the
+ * peripheral register description.
+ */
+
+#define VALUE_MASK(_val, _field) (((_val) & (_field##_V)) << (_field##_S))
+
+/* SPI Maximum buffer size in bytes */
+
+#define SPI_MAX_BUF_SIZE (64)
+
+#ifndef MIN
+#  define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* SPI Device hardware configuration */
+
+struct esp32s2_spi_config_s
+{
+  uint32_t clk_freq;          /* SPI default clock frequency */
+  uint32_t width;             /* SPI default width */
+  enum spi_mode_e mode;       /* SPI default mode */
+
+  uint8_t id;                 /* ESP32-S2 SPI device ID: SPIx {2,3} */
+  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 clk_bit;           /* Clock enable bit */
+  uint32_t rst_bit;           /* SPI reset bit */
+  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 */
+};
+
+struct esp32s2_spi_priv_s
+{
+  /* Externally visible part of the SPI interface */
+
+  struct spi_dev_s spi_dev;
+
+  /* Port configuration */
+
+  const struct esp32s2_spi_config_s *config;
+  int refs;             /* Reference count */
+  sem_t exclsem;        /* Held while chip is selected for mutual exclusion */
+  uint32_t frequency;   /* Requested clock frequency */
+  uint32_t actual;      /* Actual clock frequency */
+  enum spi_mode_e mode; /* Actual SPI hardware mode */
+  uint8_t nbits;        /* Actual SPI send/receive bits once transmission */
+  spinlock_t lock;      /* Device specific lock. */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int esp32s2_spi_lock(struct spi_dev_s *dev, bool lock);
+#ifndef CONFIG_ESP32S2_SPI_UDCS
+static void esp32s2_spi_select(struct spi_dev_s *dev,
+                               uint32_t devid, bool selected);
+#endif
+static uint32_t esp32s2_spi_setfrequency(struct spi_dev_s *dev,
+                                         uint32_t frequency);
+static void esp32s2_spi_setmode(struct spi_dev_s *dev,
+                                enum spi_mode_e mode);
+static void esp32s2_spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int esp32s2_spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t esp32s2_spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void esp32s2_spi_exchange(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 void *rxbuffer, size_t nwords);
+static void esp32s2_spi_poll_exchange(struct esp32s2_spi_priv_s *priv,
+                                      const void *txbuffer,
+                                      void *rxbuffer,
+                                      size_t nwords);
+#ifndef CONFIG_SPI_EXCHANGE
+static void esp32s2_spi_sndblock(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 size_t nwords);
+static void esp32s2_spi_recvblock(struct spi_dev_s *dev,
+                                  void *rxbuffer,
+                                  size_t nwords);
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+static int esp32s2_spi_trigger(struct spi_dev_s *dev);
+#endif
+static void esp32s2_spi_init(struct spi_dev_s *dev);
+static void esp32s2_spi_deinit(struct spi_dev_s *dev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP32S2_SPI2
+static const struct esp32s2_spi_config_s esp32s2_spi2_config =
+{
+  .clk_freq     = SPI_DEFAULT_FREQ,
+  .width        = SPI_DEFAULT_WIDTH,
+  .id           = 2,
+  .mode         = SPI_DEFAULT_MODE,
+  .cs_pin       = CONFIG_ESP32S2_SPI2_CSPIN,
+  .mosi_pin     = CONFIG_ESP32S2_SPI2_MOSIPIN,
+  .miso_pin     = CONFIG_ESP32S2_SPI2_MISOPIN,
+  .clk_pin      = CONFIG_ESP32S2_SPI2_CLKPIN,
+  .clk_bit      = SYSTEM_SPI2_CLK_EN,
+  .rst_bit      = SYSTEM_SPI2_RST,
+  .cs_insig     = FSPICS0_IN_IDX,
+  .cs_outsig    = FSPICS0_OUT_IDX,
+  .mosi_insig   = FSPID_IN_IDX,
+  .mosi_outsig  = FSPID_OUT_IDX,
+  .miso_insig   = FSPIQ_IN_IDX,
+  .miso_outsig  = FSPIQ_OUT_IDX,
+  .clk_insig    = FSPICLK_IN_IDX,
+  .clk_outsig   = FSPICLK_OUT_IDX
+};
+
+static const struct spi_ops_s esp32s2_spi2_ops =
+{
+  .lock              = esp32s2_spi_lock,
+#ifdef CONFIG_ESP32S2_SPI_UDCS
+  .select            = esp32s2_spi2_select,
+#else
+  .select            = esp32s2_spi_select,
+#endif
+  .setfrequency      = esp32s2_spi_setfrequency,
+  .setmode           = esp32s2_spi_setmode,
+  .setbits           = esp32s2_spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = esp32s2_spi_hwfeatures,
+#endif
+  .status            = esp32s2_spi2_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = esp32s2_spi2_cmddata,
+#endif
+  .send              = esp32s2_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = esp32s2_spi_exchange,
+#else
+  .sndblock          = esp32s2_spi_sndblock,
+  .recvblock         = esp32s2_spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = esp32s2_spi_trigger,
+#endif
+  .registercallback  = NULL,
+};
+
+static struct esp32s2_spi_priv_s esp32s2_spi2_priv =
+{
+  .spi_dev     =
+                {
+                  .ops = &esp32s2_spi2_ops
+                },
+  .config      = &esp32s2_spi2_config,
+  .refs        = 0,
+  .exclsem     = SEM_INITIALIZER(0),
+  .frequency   = 0,
+  .actual      = 0,
+  .mode        = 0,
+  .nbits       = 0
+};
+#endif /* CONFIG_ESP32S2_SPI2 */
+
+#ifdef CONFIG_ESP32S2_SPI3
+static const struct esp32s2_spi_config_s esp32s2_spi3_config =
+{
+  .clk_freq     = SPI_DEFAULT_FREQ,
+  .width        = SPI_DEFAULT_WIDTH,
+  .id           = 3,
+  .mode         = SPI_DEFAULT_MODE,
+  .cs_pin       = CONFIG_ESP32S2_SPI3_CSPIN,
+  .mosi_pin     = CONFIG_ESP32S2_SPI3_MOSIPIN,
+  .miso_pin     = CONFIG_ESP32S2_SPI3_MISOPIN,
+  .clk_pin      = CONFIG_ESP32S2_SPI3_CLKPIN,
+  .clk_bit      = SYSTEM_SPI3_CLK_EN,
+  .rst_bit      = SYSTEM_SPI3_RST,
+  .cs_insig     = FSPICS0_IN_IDX,
+  .cs_outsig    = FSPICS0_OUT_IDX,
+  .mosi_insig   = FSPID_IN_IDX,
+  .mosi_outsig  = FSPID_OUT_IDX,
+  .miso_insig   = FSPIQ_IN_IDX,
+  .miso_outsig  = FSPIQ_OUT_IDX,
+  .clk_insig    = FSPICLK_IN_IDX,
+  .clk_outsig   = FSPICLK_OUT_IDX
+};
+
+static const struct spi_ops_s esp32s2_spi3_ops =
+{
+  .lock              = esp32s2_spi_lock,
+#ifdef CONFIG_ESP32S2_SPI_UDCS
+  .select            = esp32s2_spi3_select,
+#else
+  .select            = esp32s2_spi_select,
+#endif
+  .setfrequency      = esp32s2_spi_setfrequency,
+  .setmode           = esp32s2_spi_setmode,
+  .setbits           = esp32s2_spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = esp32s2_spi_hwfeatures,
+#endif
+  .status            = esp32s2_spi3_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = esp32s2_spi3_cmddata,
+#endif
+  .send              = esp32s2_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = esp32s2_spi_exchange,
+#else
+  .sndblock          = esp32s2_spi_sndblock,
+  .recvblock         = esp32s2_spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = esp32s2_spi_trigger,
+#endif
+  .registercallback  = NULL,
+};
+
+static struct esp32s2_spi_priv_s esp32s2_spi3_priv =
+{
+  .spi_dev     =
+                {
+                  .ops = &esp32s2_spi3_ops
+                },
+  .config      = &esp32s2_spi3_config,
+  .refs        = 0,
+  .exclsem     = SEM_INITIALIZER(0),
+  .frequency   = 0,
+  .actual      = 0,
+  .mode        = 0,
+  .nbits       = 0
+};
+#endif /* CONFIG_ESP32S2_SPI3 */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32s2_spi_set_regbits
+ *
+ * Description:
+ *   Set the bits of the SPI register.
+ *
+ * Input Parameters:
+ *   addr   - Address of the register of interest
+ *   bits   - Bits to be set
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static inline void esp32s2_spi_set_regbits(uint32_t addr, uint32_t bits)
+{
+  uint32_t tmp = getreg32(addr);
+
+  putreg32(tmp | bits, addr);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_clr_regbits
+ *
+ * Description:
+ *   Clear the bits of the SPI register.
+ *
+ * Input Parameters:
+ *   addr   - Address of the register of interest
+ *   bits   - Bits to be cleared
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static inline void esp32s2_spi_clr_regbits(uint32_t addr, uint32_t bits)
+{
+  uint32_t tmp = getreg32(addr);
+
+  putreg32(tmp & ~bits, addr);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_iomux
+ *
+ * Description:
+ *   Check if the option SPI GPIO pins can use IOMUX directly
+ *
+ * Input Parameters:
+ *   priv   - Private SPI device structure
+ *
+ * Returned Value:
+ *   True if can use IOMUX or false if can't.
+ *
+ ****************************************************************************/
+
+static inline bool esp32s2_spi_iomux(struct esp32s2_spi_priv_s *priv)
+{
+  bool mapped = false;
+  const struct esp32s2_spi_config_s *cfg = priv->config;
+
+  /* We only need to check SPI2, SPI3 doesn't support IOMUX */
+
+  if (cfg->id == 2)
+    {
+      if (cfg->mosi_pin == SPI2_IOMUX_MOSIPIN &&
+#ifndef CONFIG_ESP32S2_SPI_SWCS
+          cfg->cs_pin == SPI2_IOMUX_CSPIN &&
+#endif
+          cfg->miso_pin == SPI2_IOMUX_MISOPIN &&
+          cfg->clk_pin == SPI2_IOMUX_CLKPIN)
+        {
+          mapped = true;
+        }
+    }
+
+  return mapped;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_lock
+ *
+ * Description:
+ *   Lock or unlock the SPI device.
+ *
+ * Input Parameters:
+ *   dev    - Device-specific state data
+ *   lock   - true: Lock SPI bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   The result of lock or unlock the SPI device.
+ *
+ ****************************************************************************/
+
+static int esp32s2_spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  int ret;
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_select
+ *
+ * Description:
+ *   Enable/disable the SPI chip select. The implementation of this method
+ *   must include handshaking: If a device is selected, it must hold off
+ *   all other attempts to select the device until the device is deselected.
+ *
+ *   If ESP32S2_SPI_SWCS is disabled, the driver will use hardware CS so that
+ *   once transmission is started the hardware selects the device and when
+ *   this transmission is done hardware deselects the device automatically.
+ *   So, this function will do nothing.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   devid    - Identifies the device to select
+ *   selected - true: slave selected, false: slave de-selected
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_ESP32S2_SPI_UDCS
+static void esp32s2_spi_select(struct spi_dev_s *dev,
+                               uint32_t devid, bool selected)
+{
+#if SPI_HAVE_SWCS
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  esp32s2_gpiowrite(priv->config->cs_pin, !selected);
+#endif
+
+  spiinfo("devid: %08" PRIx32 " CS: %s\n",
+          devid, selected ? "select" : "free");
+}
+#endif
+
+/****************************************************************************
+ * Name: esp32s2_spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev       - Device-specific state data
+ *   frequency - The requested SPI frequency
+ *
+ * Returned Value:
+ *   Returns the current selected frequency.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_setfrequency(struct spi_dev_s *dev,
+                                         uint32_t frequency)
+{
+  uint32_t reg_val;
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+  const uint32_t duty_cycle = 128;
+
+  if (priv->frequency == frequency)
+    {
+      /* Requested frequency is the same as the current frequency. */
+
+      return priv->actual;
+    }
+
+  /* In HW, n, h and l fields range from 1 to 64, pre ranges from 1 to 8K.
+   * The value written to register is one lower than the used value.
+   */
+
+  if (frequency > ((APB_CLK_FREQ / 4) * 3))
+    {
+      /* Using APB frequency directly will give us the best result here. */
+
+      reg_val = SPI_CLK_EQU_SYSCLK_M;
+      priv->actual = APB_CLK_FREQ;
+    }
+  else
+    {
+      /* For best duty cycle resolution, we want n to be as close to 32 as
+       * possible, but we also need a pre/n combo that gets us as close as
+       * possible to the intended frequency. To do this, we bruteforce n and
+       * calculate the best pre to go along with that. If there's a choice
+       * between pre/n combos that give the same result, use the one with the
+       * higher n.
+       */
+
+      int32_t pre;
+      int32_t n;
+      int32_t h;
+      int32_t l;
+      int32_t bestn = -1;
+      int32_t bestpre = -1;
+      int32_t besterr = 0;
+      int32_t errval;
+
+      /* Start at n = 2. We need to be able to set h/l so we have at least
+       * one high and one low pulse.
+       */
+
+      for (n = 2; n <= 64; n++)
+        {
+          /* Effectively, this does:
+           *   pre = round((APB_CLK_FREQ / n) / frequency)
+           */
+
+          pre = ((APB_CLK_FREQ / n) + (frequency / 2)) / frequency;
+
+          if (pre <= 0)
+            {
+              pre = 1;
+            }
+
+          if (pre > 16)
+            {
+              pre = 16;
+            }
+
+          errval = abs(APB_CLK_FREQ / (pre * n) - frequency);
+          if (bestn == -1 || errval <= besterr)
+            {
+              besterr = errval;
+              bestn = n;
+              bestpre = pre;
+            }
+        }
+
+      n = bestn;
+      pre = bestpre;
+      l = n;
+
+      /* Effectively, this does:
+       *   h = round((duty_cycle * n) / 256)
+       */
+
+      h = (duty_cycle * n + 127) / 256;
+      if (h <= 0)
+        {
+          h = 1;
+        }
+
+      reg_val = ((l - 1) << SPI_CLKCNT_L_S) |
+                ((h - 1) << SPI_CLKCNT_H_S) |
+                ((n - 1) << SPI_CLKCNT_N_S) |
+                ((pre - 1) << SPI_CLKDIV_PRE_S);
+
+      priv->actual = APB_CLK_FREQ / (n * pre);
+    }
+
+  priv->frequency = frequency;
+
+  putreg32(reg_val, SPI_CLOCK_REG(priv->config->id));
+
+  spiinfo("frequency=%" PRIu32 ", actual=%" PRIu32 "\n",
+          priv->frequency, priv->actual);
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The requested SPI mode
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_setmode(struct spi_dev_s *dev,
+                                enum spi_mode_e mode)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      uint32_t ck_idle_edge;
+      uint32_t ck_out_edge;
+
+      switch (mode)
+        {
+          case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+            ck_idle_edge = 0;
+            ck_out_edge = 0;
+            break;
+
+          case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+            ck_idle_edge = 0;
+            ck_out_edge = 1;
+            break;
+
+          case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+            ck_idle_edge = 1;
+            ck_out_edge = 1;
+            break;
+
+          case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+            ck_idle_edge = 1;
+            ck_out_edge = 0;
+            break;
+
+          default:
+            spierr("Invalid mode: %d\n", mode);
+            DEBUGPANIC();
+            return;
+        }
+
+      esp32s2_spi_clr_regbits(SPI_MISC_REG(priv->config->id),
+                              SPI_CK_IDLE_EDGE_M);
+      esp32s2_spi_set_regbits(SPI_MISC_REG(priv->config->id),
+                              VALUE_MASK(ck_idle_edge, SPI_CK_IDLE_EDGE));
+
+      esp32s2_spi_clr_regbits(SPI_USER_REG(priv->config->id),
+                              SPI_CK_OUT_EDGE_M);
+      esp32s2_spi_set_regbits(SPI_USER_REG(priv->config->id),
+                              VALUE_MASK(ck_out_edge, SPI_CK_OUT_EDGE));
+
+      priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_setbits
+ *
+ * Description:
+ *   Set the number of bits per word.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *   nbits - The number of bits in an SPI word.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  spiinfo("nbits=%d\n", nbits);
+
+  priv->nbits = nbits;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_hwfeatures
+ *
+ * Description:
+ *   Set hardware-specific feature flags.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   features - H/W feature flags
+ *
+ * Returned Value:
+ *   Zero (OK) if the selected H/W features are enabled; A negated errno
+ *   value if any H/W feature is not supportable.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_HWFEATURES
+static int esp32s2_spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features)
+{
+  /* Other H/W features are not supported */
+
+  return (features == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp32s2_spi_poll_send
+ *
+ * Description:
+ *   Send one word on SPI by polling mode.
+ *
+ * Input Parameters:
+ *   priv - SPI private state data
+ *   wd   - The word to send. The size of the data is determined by the
+ *          number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   Received value.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_poll_send(struct esp32s2_spi_priv_s *priv,
+                                      uint32_t wd)
+{
+  uint32_t val;
+
+  const uintptr_t spi_miso_dlen_reg = SPI_MISO_DLEN_REG(priv->config->id);
+  const uintptr_t spi_mosi_dlen_reg = SPI_MOSI_DLEN_REG(priv->config->id);
+  const uintptr_t spi_w0_reg = SPI_W0_REG(priv->config->id);
+  const uintptr_t spi_cmd_reg = SPI_CMD_REG(priv->config->id);
+
+  putreg32((priv->nbits - 1), spi_miso_dlen_reg);
+  putreg32((priv->nbits - 1), spi_mosi_dlen_reg);
+
+  putreg32(wd, spi_w0_reg);
+
+  esp32s2_spi_set_regbits(spi_cmd_reg, SPI_USR_M);
+
+  while ((getreg32(spi_cmd_reg) & SPI_USR_M) != 0)
+    {
+      ;
+    }
+
+  val = getreg32(spi_w0_reg);
+
+  spiinfo("send=0x%" PRIx32 " and recv=0x%" PRIx32 "\n", wd, val);
+
+  return val;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_send
+ *
+ * Description:
+ *   Send one word on SPI.
+ *
+ * Input Parameters:
+ *   dev - Device-specific state data
+ *   wd  - The word to send. The size of the data is determined by the
+ *         number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   Received value.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  return esp32s2_spi_poll_send(priv, wd);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_poll_exchange
+ *
+ * Description:
+ *   Exchange a block of data from SPI.
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_poll_exchange(struct esp32s2_spi_priv_s *priv,
+                                      const void *txbuffer,
+                                      void *rxbuffer,
+                                      size_t nwords)
+{
+  const uint32_t total_bytes = nwords * (priv->nbits / 8);
+  uintptr_t bytes_remaining = total_bytes;
+  const uint8_t *tp = (const uint8_t *)txbuffer;
+  uint8_t *rp = (uint8_t *)rxbuffer;
+
+  while (bytes_remaining != 0)
+    {
+      /* Initialize data_buf_reg with the address of the first data buffer
+       * register (W0).
+       */
+
+      uintptr_t data_buf_reg = SPI_W0_REG(priv->config->id);
+      uint32_t transfer_size = MIN(SPI_MAX_BUF_SIZE, bytes_remaining);
+
+      /* Write data words to data buffer registers.
+       * SPI peripheral contains 16 registers (W0 - W15).
+       */
+
+      for (int i = 0 ; i < transfer_size; i += sizeof(uintptr_t))
+        {
+          uint32_t w_wd = UINT32_MAX;
+
+          if (tp != NULL)
+            {
+              memcpy(&w_wd, tp, sizeof(uintptr_t));
+
+              tp += sizeof(uintptr_t);
+            }
+
+          putreg32(w_wd, data_buf_reg);
+
+          spiinfo("send=0x%" PRIx32 " data_reg=0x%" PRIxPTR "\n",
+                  w_wd, data_buf_reg);
+
+          /* Update data_buf_reg to point to the next data buffer register. */
+
+          data_buf_reg += sizeof(uintptr_t);
+        }
+
+      esp32s2_spi_set_regbits(SPI_USER_REG(priv->config->id),
+                              SPI_USR_MOSI_M);
+
+      if (rp == NULL)
+        {
+          esp32s2_spi_clr_regbits(SPI_USER_REG(priv->config->id),
+                                  SPI_USR_MISO_M);
+        }
+      else
+        {
+          esp32s2_spi_set_regbits(SPI_USER_REG(priv->config->id),
+                                  SPI_USR_MISO_M);
+        }
+
+      putreg32((transfer_size * 8) - 1,
+               SPI_MOSI_DLEN_REG(priv->config->id));
+
+      putreg32((transfer_size * 8) - 1,
+               SPI_MISO_DLEN_REG(priv->config->id));
+
+      /* Trigger start of user-defined transaction for master. */
+
+      esp32s2_spi_set_regbits(SPI_CMD_REG(priv->config->id), SPI_USR_M);
+
+      /* Wait for the user-defined transaction to finish. */
+
+      while ((getreg32(SPI_CMD_REG(priv->config->id)) & SPI_USR_M) != 0)
+        {
+          ;
+        }
+
+      if (rp != NULL)
+        {
+          /* Set data_buf_reg with the address of the first data buffer
+           * register (W0).
+           */
+
+          data_buf_reg = SPI_W0_REG(priv->config->id);
+
+          /* Read received data words from SPI data buffer registers. */
+
+          for (int i = 0 ; i < transfer_size; i += sizeof(uintptr_t))
+            {
+              uint32_t r_wd = getreg32(data_buf_reg);

Review Comment:
   ```suggestion
                 uintptr_t r_wd = getreg32(data_buf_reg);
   ```



##########
arch/xtensa/src/esp32s2/esp32s2_spi.c:
##########
@@ -0,0 +1,1231 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32s2/esp32s2_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_ESP32S2_SPI
+
+#include <assert.h>
+#include <debug.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/spi/spi.h>
+
+#include <arch/board/board.h>
+
+#include "esp32s2_spi.h"
+#include "esp32s2_irq.h"
+#include "esp32s2_gpio.h"
+
+#include "xtensa.h"
+#include "hardware/esp32s2_gpio_sigmap.h"
+#include "hardware/esp32s2_pinmap.h"
+#include "hardware/esp32s2_spi.h"
+#include "hardware/esp32s2_soc.h"
+#include "hardware/esp32s2_system.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Check if Chip-Select pin will be controlled via software */
+
+#ifdef CONFIG_ESP32S2_SPI_SWCS
+#  define SPI_HAVE_SWCS 1
+#else
+#  define SPI_HAVE_SWCS 0
+#endif
+
+/* SPI default frequency (limited by clock divider) */
+
+#define SPI_DEFAULT_FREQ  (400000)
+
+/* SPI default width */
+
+#define SPI_DEFAULT_WIDTH (8)
+
+/* SPI default mode */
+
+#define SPI_DEFAULT_MODE  (SPIDEV_MODE0)
+
+/* Helper for applying the mask for a given register field.
+ * Mask is determined by the macros suffixed with _V and _S from the
+ * peripheral register description.
+ */
+
+#define VALUE_MASK(_val, _field) (((_val) & (_field##_V)) << (_field##_S))
+
+/* SPI Maximum buffer size in bytes */
+
+#define SPI_MAX_BUF_SIZE (64)
+
+#ifndef MIN
+#  define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* SPI Device hardware configuration */
+
+struct esp32s2_spi_config_s
+{
+  uint32_t clk_freq;          /* SPI default clock frequency */
+  uint32_t width;             /* SPI default width */
+  enum spi_mode_e mode;       /* SPI default mode */
+
+  uint8_t id;                 /* ESP32-S2 SPI device ID: SPIx {2,3} */
+  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 clk_bit;           /* Clock enable bit */
+  uint32_t rst_bit;           /* SPI reset bit */
+  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 */
+};
+
+struct esp32s2_spi_priv_s
+{
+  /* Externally visible part of the SPI interface */
+
+  struct spi_dev_s spi_dev;
+
+  /* Port configuration */
+
+  const struct esp32s2_spi_config_s *config;
+  int refs;             /* Reference count */
+  sem_t exclsem;        /* Held while chip is selected for mutual exclusion */
+  uint32_t frequency;   /* Requested clock frequency */
+  uint32_t actual;      /* Actual clock frequency */
+  enum spi_mode_e mode; /* Actual SPI hardware mode */
+  uint8_t nbits;        /* Actual SPI send/receive bits once transmission */
+  spinlock_t lock;      /* Device specific lock. */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int esp32s2_spi_lock(struct spi_dev_s *dev, bool lock);
+#ifndef CONFIG_ESP32S2_SPI_UDCS
+static void esp32s2_spi_select(struct spi_dev_s *dev,
+                               uint32_t devid, bool selected);
+#endif
+static uint32_t esp32s2_spi_setfrequency(struct spi_dev_s *dev,
+                                         uint32_t frequency);
+static void esp32s2_spi_setmode(struct spi_dev_s *dev,
+                                enum spi_mode_e mode);
+static void esp32s2_spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int esp32s2_spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t esp32s2_spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void esp32s2_spi_exchange(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 void *rxbuffer, size_t nwords);
+static void esp32s2_spi_poll_exchange(struct esp32s2_spi_priv_s *priv,
+                                      const void *txbuffer,
+                                      void *rxbuffer,
+                                      size_t nwords);
+#ifndef CONFIG_SPI_EXCHANGE
+static void esp32s2_spi_sndblock(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 size_t nwords);
+static void esp32s2_spi_recvblock(struct spi_dev_s *dev,
+                                  void *rxbuffer,
+                                  size_t nwords);
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+static int esp32s2_spi_trigger(struct spi_dev_s *dev);
+#endif
+static void esp32s2_spi_init(struct spi_dev_s *dev);
+static void esp32s2_spi_deinit(struct spi_dev_s *dev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP32S2_SPI2
+static const struct esp32s2_spi_config_s esp32s2_spi2_config =
+{
+  .clk_freq     = SPI_DEFAULT_FREQ,
+  .width        = SPI_DEFAULT_WIDTH,
+  .id           = 2,
+  .mode         = SPI_DEFAULT_MODE,
+  .cs_pin       = CONFIG_ESP32S2_SPI2_CSPIN,
+  .mosi_pin     = CONFIG_ESP32S2_SPI2_MOSIPIN,
+  .miso_pin     = CONFIG_ESP32S2_SPI2_MISOPIN,
+  .clk_pin      = CONFIG_ESP32S2_SPI2_CLKPIN,
+  .clk_bit      = SYSTEM_SPI2_CLK_EN,
+  .rst_bit      = SYSTEM_SPI2_RST,
+  .cs_insig     = FSPICS0_IN_IDX,
+  .cs_outsig    = FSPICS0_OUT_IDX,
+  .mosi_insig   = FSPID_IN_IDX,
+  .mosi_outsig  = FSPID_OUT_IDX,
+  .miso_insig   = FSPIQ_IN_IDX,
+  .miso_outsig  = FSPIQ_OUT_IDX,
+  .clk_insig    = FSPICLK_IN_IDX,
+  .clk_outsig   = FSPICLK_OUT_IDX
+};
+
+static const struct spi_ops_s esp32s2_spi2_ops =
+{
+  .lock              = esp32s2_spi_lock,
+#ifdef CONFIG_ESP32S2_SPI_UDCS
+  .select            = esp32s2_spi2_select,
+#else
+  .select            = esp32s2_spi_select,
+#endif
+  .setfrequency      = esp32s2_spi_setfrequency,
+  .setmode           = esp32s2_spi_setmode,
+  .setbits           = esp32s2_spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = esp32s2_spi_hwfeatures,
+#endif
+  .status            = esp32s2_spi2_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = esp32s2_spi2_cmddata,
+#endif
+  .send              = esp32s2_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = esp32s2_spi_exchange,
+#else
+  .sndblock          = esp32s2_spi_sndblock,
+  .recvblock         = esp32s2_spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = esp32s2_spi_trigger,
+#endif
+  .registercallback  = NULL,
+};
+
+static struct esp32s2_spi_priv_s esp32s2_spi2_priv =
+{
+  .spi_dev     =
+                {
+                  .ops = &esp32s2_spi2_ops
+                },
+  .config      = &esp32s2_spi2_config,
+  .refs        = 0,
+  .exclsem     = SEM_INITIALIZER(0),
+  .frequency   = 0,
+  .actual      = 0,
+  .mode        = 0,
+  .nbits       = 0
+};
+#endif /* CONFIG_ESP32S2_SPI2 */
+
+#ifdef CONFIG_ESP32S2_SPI3
+static const struct esp32s2_spi_config_s esp32s2_spi3_config =
+{
+  .clk_freq     = SPI_DEFAULT_FREQ,
+  .width        = SPI_DEFAULT_WIDTH,
+  .id           = 3,
+  .mode         = SPI_DEFAULT_MODE,
+  .cs_pin       = CONFIG_ESP32S2_SPI3_CSPIN,
+  .mosi_pin     = CONFIG_ESP32S2_SPI3_MOSIPIN,
+  .miso_pin     = CONFIG_ESP32S2_SPI3_MISOPIN,
+  .clk_pin      = CONFIG_ESP32S2_SPI3_CLKPIN,
+  .clk_bit      = SYSTEM_SPI3_CLK_EN,
+  .rst_bit      = SYSTEM_SPI3_RST,
+  .cs_insig     = FSPICS0_IN_IDX,
+  .cs_outsig    = FSPICS0_OUT_IDX,
+  .mosi_insig   = FSPID_IN_IDX,
+  .mosi_outsig  = FSPID_OUT_IDX,
+  .miso_insig   = FSPIQ_IN_IDX,
+  .miso_outsig  = FSPIQ_OUT_IDX,
+  .clk_insig    = FSPICLK_IN_IDX,
+  .clk_outsig   = FSPICLK_OUT_IDX
+};
+
+static const struct spi_ops_s esp32s2_spi3_ops =
+{
+  .lock              = esp32s2_spi_lock,
+#ifdef CONFIG_ESP32S2_SPI_UDCS
+  .select            = esp32s2_spi3_select,
+#else
+  .select            = esp32s2_spi_select,
+#endif
+  .setfrequency      = esp32s2_spi_setfrequency,
+  .setmode           = esp32s2_spi_setmode,
+  .setbits           = esp32s2_spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = esp32s2_spi_hwfeatures,
+#endif
+  .status            = esp32s2_spi3_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = esp32s2_spi3_cmddata,
+#endif
+  .send              = esp32s2_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = esp32s2_spi_exchange,
+#else
+  .sndblock          = esp32s2_spi_sndblock,
+  .recvblock         = esp32s2_spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = esp32s2_spi_trigger,
+#endif
+  .registercallback  = NULL,
+};
+
+static struct esp32s2_spi_priv_s esp32s2_spi3_priv =
+{
+  .spi_dev     =
+                {
+                  .ops = &esp32s2_spi3_ops
+                },
+  .config      = &esp32s2_spi3_config,
+  .refs        = 0,
+  .exclsem     = SEM_INITIALIZER(0),
+  .frequency   = 0,
+  .actual      = 0,
+  .mode        = 0,
+  .nbits       = 0
+};
+#endif /* CONFIG_ESP32S2_SPI3 */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32s2_spi_set_regbits
+ *
+ * Description:
+ *   Set the bits of the SPI register.
+ *
+ * Input Parameters:
+ *   addr   - Address of the register of interest
+ *   bits   - Bits to be set
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static inline void esp32s2_spi_set_regbits(uint32_t addr, uint32_t bits)
+{
+  uint32_t tmp = getreg32(addr);
+
+  putreg32(tmp | bits, addr);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_clr_regbits
+ *
+ * Description:
+ *   Clear the bits of the SPI register.
+ *
+ * Input Parameters:
+ *   addr   - Address of the register of interest
+ *   bits   - Bits to be cleared
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static inline void esp32s2_spi_clr_regbits(uint32_t addr, uint32_t bits)
+{
+  uint32_t tmp = getreg32(addr);
+
+  putreg32(tmp & ~bits, addr);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_iomux
+ *
+ * Description:
+ *   Check if the option SPI GPIO pins can use IOMUX directly
+ *
+ * Input Parameters:
+ *   priv   - Private SPI device structure
+ *
+ * Returned Value:
+ *   True if can use IOMUX or false if can't.
+ *
+ ****************************************************************************/
+
+static inline bool esp32s2_spi_iomux(struct esp32s2_spi_priv_s *priv)
+{
+  bool mapped = false;
+  const struct esp32s2_spi_config_s *cfg = priv->config;
+
+  /* We only need to check SPI2, SPI3 doesn't support IOMUX */
+
+  if (cfg->id == 2)
+    {
+      if (cfg->mosi_pin == SPI2_IOMUX_MOSIPIN &&
+#ifndef CONFIG_ESP32S2_SPI_SWCS
+          cfg->cs_pin == SPI2_IOMUX_CSPIN &&
+#endif
+          cfg->miso_pin == SPI2_IOMUX_MISOPIN &&
+          cfg->clk_pin == SPI2_IOMUX_CLKPIN)
+        {
+          mapped = true;
+        }
+    }
+
+  return mapped;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_lock
+ *
+ * Description:
+ *   Lock or unlock the SPI device.
+ *
+ * Input Parameters:
+ *   dev    - Device-specific state data
+ *   lock   - true: Lock SPI bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   The result of lock or unlock the SPI device.
+ *
+ ****************************************************************************/
+
+static int esp32s2_spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  int ret;
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_select
+ *
+ * Description:
+ *   Enable/disable the SPI chip select. The implementation of this method
+ *   must include handshaking: If a device is selected, it must hold off
+ *   all other attempts to select the device until the device is deselected.
+ *
+ *   If ESP32S2_SPI_SWCS is disabled, the driver will use hardware CS so that
+ *   once transmission is started the hardware selects the device and when
+ *   this transmission is done hardware deselects the device automatically.
+ *   So, this function will do nothing.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   devid    - Identifies the device to select
+ *   selected - true: slave selected, false: slave de-selected
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_ESP32S2_SPI_UDCS
+static void esp32s2_spi_select(struct spi_dev_s *dev,
+                               uint32_t devid, bool selected)
+{
+#if SPI_HAVE_SWCS
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  esp32s2_gpiowrite(priv->config->cs_pin, !selected);
+#endif
+
+  spiinfo("devid: %08" PRIx32 " CS: %s\n",
+          devid, selected ? "select" : "free");
+}
+#endif
+
+/****************************************************************************
+ * Name: esp32s2_spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev       - Device-specific state data
+ *   frequency - The requested SPI frequency
+ *
+ * Returned Value:
+ *   Returns the current selected frequency.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_setfrequency(struct spi_dev_s *dev,
+                                         uint32_t frequency)
+{
+  uint32_t reg_val;
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+  const uint32_t duty_cycle = 128;
+
+  if (priv->frequency == frequency)
+    {
+      /* Requested frequency is the same as the current frequency. */
+
+      return priv->actual;
+    }
+
+  /* In HW, n, h and l fields range from 1 to 64, pre ranges from 1 to 8K.
+   * The value written to register is one lower than the used value.
+   */
+
+  if (frequency > ((APB_CLK_FREQ / 4) * 3))
+    {
+      /* Using APB frequency directly will give us the best result here. */
+
+      reg_val = SPI_CLK_EQU_SYSCLK_M;
+      priv->actual = APB_CLK_FREQ;
+    }
+  else
+    {
+      /* For best duty cycle resolution, we want n to be as close to 32 as
+       * possible, but we also need a pre/n combo that gets us as close as
+       * possible to the intended frequency. To do this, we bruteforce n and
+       * calculate the best pre to go along with that. If there's a choice
+       * between pre/n combos that give the same result, use the one with the
+       * higher n.
+       */
+
+      int32_t pre;
+      int32_t n;
+      int32_t h;
+      int32_t l;
+      int32_t bestn = -1;
+      int32_t bestpre = -1;
+      int32_t besterr = 0;
+      int32_t errval;
+
+      /* Start at n = 2. We need to be able to set h/l so we have at least
+       * one high and one low pulse.
+       */
+
+      for (n = 2; n <= 64; n++)
+        {
+          /* Effectively, this does:
+           *   pre = round((APB_CLK_FREQ / n) / frequency)
+           */
+
+          pre = ((APB_CLK_FREQ / n) + (frequency / 2)) / frequency;
+
+          if (pre <= 0)
+            {
+              pre = 1;
+            }
+
+          if (pre > 16)
+            {
+              pre = 16;
+            }
+
+          errval = abs(APB_CLK_FREQ / (pre * n) - frequency);
+          if (bestn == -1 || errval <= besterr)
+            {
+              besterr = errval;
+              bestn = n;
+              bestpre = pre;
+            }
+        }
+
+      n = bestn;
+      pre = bestpre;
+      l = n;
+
+      /* Effectively, this does:
+       *   h = round((duty_cycle * n) / 256)
+       */
+
+      h = (duty_cycle * n + 127) / 256;
+      if (h <= 0)
+        {
+          h = 1;
+        }
+
+      reg_val = ((l - 1) << SPI_CLKCNT_L_S) |
+                ((h - 1) << SPI_CLKCNT_H_S) |
+                ((n - 1) << SPI_CLKCNT_N_S) |
+                ((pre - 1) << SPI_CLKDIV_PRE_S);
+
+      priv->actual = APB_CLK_FREQ / (n * pre);
+    }
+
+  priv->frequency = frequency;
+
+  putreg32(reg_val, SPI_CLOCK_REG(priv->config->id));
+
+  spiinfo("frequency=%" PRIu32 ", actual=%" PRIu32 "\n",
+          priv->frequency, priv->actual);
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The requested SPI mode
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_setmode(struct spi_dev_s *dev,
+                                enum spi_mode_e mode)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      uint32_t ck_idle_edge;
+      uint32_t ck_out_edge;
+
+      switch (mode)
+        {
+          case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+            ck_idle_edge = 0;
+            ck_out_edge = 0;
+            break;
+
+          case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+            ck_idle_edge = 0;
+            ck_out_edge = 1;
+            break;
+
+          case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+            ck_idle_edge = 1;
+            ck_out_edge = 1;
+            break;
+
+          case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+            ck_idle_edge = 1;
+            ck_out_edge = 0;
+            break;
+
+          default:
+            spierr("Invalid mode: %d\n", mode);
+            DEBUGPANIC();
+            return;
+        }
+
+      esp32s2_spi_clr_regbits(SPI_MISC_REG(priv->config->id),
+                              SPI_CK_IDLE_EDGE_M);
+      esp32s2_spi_set_regbits(SPI_MISC_REG(priv->config->id),
+                              VALUE_MASK(ck_idle_edge, SPI_CK_IDLE_EDGE));
+
+      esp32s2_spi_clr_regbits(SPI_USER_REG(priv->config->id),
+                              SPI_CK_OUT_EDGE_M);
+      esp32s2_spi_set_regbits(SPI_USER_REG(priv->config->id),
+                              VALUE_MASK(ck_out_edge, SPI_CK_OUT_EDGE));
+
+      priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_setbits
+ *
+ * Description:
+ *   Set the number of bits per word.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *   nbits - The number of bits in an SPI word.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  spiinfo("nbits=%d\n", nbits);
+
+  priv->nbits = nbits;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_hwfeatures
+ *
+ * Description:
+ *   Set hardware-specific feature flags.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   features - H/W feature flags
+ *
+ * Returned Value:
+ *   Zero (OK) if the selected H/W features are enabled; A negated errno
+ *   value if any H/W feature is not supportable.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_HWFEATURES
+static int esp32s2_spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features)
+{
+  /* Other H/W features are not supported */
+
+  return (features == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp32s2_spi_poll_send
+ *
+ * Description:
+ *   Send one word on SPI by polling mode.
+ *
+ * Input Parameters:
+ *   priv - SPI private state data
+ *   wd   - The word to send. The size of the data is determined by the
+ *          number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   Received value.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_poll_send(struct esp32s2_spi_priv_s *priv,
+                                      uint32_t wd)
+{
+  uint32_t val;
+
+  const uintptr_t spi_miso_dlen_reg = SPI_MISO_DLEN_REG(priv->config->id);
+  const uintptr_t spi_mosi_dlen_reg = SPI_MOSI_DLEN_REG(priv->config->id);
+  const uintptr_t spi_w0_reg = SPI_W0_REG(priv->config->id);
+  const uintptr_t spi_cmd_reg = SPI_CMD_REG(priv->config->id);
+
+  putreg32((priv->nbits - 1), spi_miso_dlen_reg);
+  putreg32((priv->nbits - 1), spi_mosi_dlen_reg);
+
+  putreg32(wd, spi_w0_reg);
+
+  esp32s2_spi_set_regbits(spi_cmd_reg, SPI_USR_M);
+
+  while ((getreg32(spi_cmd_reg) & SPI_USR_M) != 0)
+    {
+      ;
+    }
+
+  val = getreg32(spi_w0_reg);
+
+  spiinfo("send=0x%" PRIx32 " and recv=0x%" PRIx32 "\n", wd, val);
+
+  return val;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_send
+ *
+ * Description:
+ *   Send one word on SPI.
+ *
+ * Input Parameters:
+ *   dev - Device-specific state data
+ *   wd  - The word to send. The size of the data is determined by the
+ *         number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   Received value.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  return esp32s2_spi_poll_send(priv, wd);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_poll_exchange
+ *
+ * Description:
+ *   Exchange a block of data from SPI.
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_poll_exchange(struct esp32s2_spi_priv_s *priv,
+                                      const void *txbuffer,
+                                      void *rxbuffer,
+                                      size_t nwords)
+{
+  const uint32_t total_bytes = nwords * (priv->nbits / 8);
+  uintptr_t bytes_remaining = total_bytes;
+  const uint8_t *tp = (const uint8_t *)txbuffer;
+  uint8_t *rp = (uint8_t *)rxbuffer;
+
+  while (bytes_remaining != 0)
+    {
+      /* Initialize data_buf_reg with the address of the first data buffer
+       * register (W0).
+       */
+
+      uintptr_t data_buf_reg = SPI_W0_REG(priv->config->id);
+      uint32_t transfer_size = MIN(SPI_MAX_BUF_SIZE, bytes_remaining);
+
+      /* Write data words to data buffer registers.
+       * SPI peripheral contains 16 registers (W0 - W15).
+       */
+
+      for (int i = 0 ; i < transfer_size; i += sizeof(uintptr_t))
+        {
+          uint32_t w_wd = UINT32_MAX;

Review Comment:
   ```suggestion
             uintptr_t w_wd = UINT32_MAX;
   ```
   or change bellow to
   ```
                 memcpy(&w_wd, tp, sizeof(uint32_t));
   
                 tp += sizeof(uint32_t);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] gustavonihei commented on a diff in pull request #6823: xtensa/esp32s2: Add basic support to SPI

Posted by GitBox <gi...@apache.org>.
gustavonihei commented on code in PR #6823:
URL: https://github.com/apache/incubator-nuttx/pull/6823#discussion_r942391731


##########
arch/xtensa/src/esp32s2/esp32s2_spi.c:
##########
@@ -0,0 +1,1231 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32s2/esp32s2_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_ESP32S2_SPI
+
+#include <assert.h>
+#include <debug.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/spi/spi.h>
+
+#include <arch/board/board.h>
+
+#include "esp32s2_spi.h"
+#include "esp32s2_irq.h"
+#include "esp32s2_gpio.h"
+
+#include "xtensa.h"
+#include "hardware/esp32s2_gpio_sigmap.h"
+#include "hardware/esp32s2_pinmap.h"
+#include "hardware/esp32s2_spi.h"
+#include "hardware/esp32s2_soc.h"
+#include "hardware/esp32s2_system.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Check if Chip-Select pin will be controlled via software */
+
+#ifdef CONFIG_ESP32S2_SPI_SWCS
+#  define SPI_HAVE_SWCS 1
+#else
+#  define SPI_HAVE_SWCS 0
+#endif
+
+/* SPI default frequency (limited by clock divider) */
+
+#define SPI_DEFAULT_FREQ  (400000)
+
+/* SPI default width */
+
+#define SPI_DEFAULT_WIDTH (8)
+
+/* SPI default mode */
+
+#define SPI_DEFAULT_MODE  (SPIDEV_MODE0)
+
+/* Helper for applying the mask for a given register field.
+ * Mask is determined by the macros suffixed with _V and _S from the
+ * peripheral register description.
+ */
+
+#define VALUE_MASK(_val, _field) (((_val) & (_field##_V)) << (_field##_S))
+
+/* SPI Maximum buffer size in bytes */
+
+#define SPI_MAX_BUF_SIZE (64)
+
+#ifndef MIN
+#  define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* SPI Device hardware configuration */
+
+struct esp32s2_spi_config_s
+{
+  uint32_t clk_freq;          /* SPI default clock frequency */
+  uint32_t width;             /* SPI default width */
+  enum spi_mode_e mode;       /* SPI default mode */
+
+  uint8_t id;                 /* ESP32-S2 SPI device ID: SPIx {2,3} */
+  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 clk_bit;           /* Clock enable bit */
+  uint32_t rst_bit;           /* SPI reset bit */
+  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 */
+};
+
+struct esp32s2_spi_priv_s
+{
+  /* Externally visible part of the SPI interface */
+
+  struct spi_dev_s spi_dev;
+
+  /* Port configuration */
+
+  const struct esp32s2_spi_config_s *config;
+  int refs;             /* Reference count */
+  sem_t exclsem;        /* Held while chip is selected for mutual exclusion */
+  uint32_t frequency;   /* Requested clock frequency */
+  uint32_t actual;      /* Actual clock frequency */
+  enum spi_mode_e mode; /* Actual SPI hardware mode */
+  uint8_t nbits;        /* Actual SPI send/receive bits once transmission */
+  spinlock_t lock;      /* Device specific lock. */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int esp32s2_spi_lock(struct spi_dev_s *dev, bool lock);
+#ifndef CONFIG_ESP32S2_SPI_UDCS
+static void esp32s2_spi_select(struct spi_dev_s *dev,
+                               uint32_t devid, bool selected);
+#endif
+static uint32_t esp32s2_spi_setfrequency(struct spi_dev_s *dev,
+                                         uint32_t frequency);
+static void esp32s2_spi_setmode(struct spi_dev_s *dev,
+                                enum spi_mode_e mode);
+static void esp32s2_spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int esp32s2_spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t esp32s2_spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void esp32s2_spi_exchange(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 void *rxbuffer, size_t nwords);
+static void esp32s2_spi_poll_exchange(struct esp32s2_spi_priv_s *priv,
+                                      const void *txbuffer,
+                                      void *rxbuffer,
+                                      size_t nwords);
+#ifndef CONFIG_SPI_EXCHANGE
+static void esp32s2_spi_sndblock(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 size_t nwords);
+static void esp32s2_spi_recvblock(struct spi_dev_s *dev,
+                                  void *rxbuffer,
+                                  size_t nwords);
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+static int esp32s2_spi_trigger(struct spi_dev_s *dev);
+#endif
+static void esp32s2_spi_init(struct spi_dev_s *dev);
+static void esp32s2_spi_deinit(struct spi_dev_s *dev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP32S2_SPI2
+static const struct esp32s2_spi_config_s esp32s2_spi2_config =
+{
+  .clk_freq     = SPI_DEFAULT_FREQ,
+  .width        = SPI_DEFAULT_WIDTH,
+  .id           = 2,
+  .mode         = SPI_DEFAULT_MODE,
+  .cs_pin       = CONFIG_ESP32S2_SPI2_CSPIN,
+  .mosi_pin     = CONFIG_ESP32S2_SPI2_MOSIPIN,
+  .miso_pin     = CONFIG_ESP32S2_SPI2_MISOPIN,
+  .clk_pin      = CONFIG_ESP32S2_SPI2_CLKPIN,
+  .clk_bit      = SYSTEM_SPI2_CLK_EN,
+  .rst_bit      = SYSTEM_SPI2_RST,
+  .cs_insig     = FSPICS0_IN_IDX,
+  .cs_outsig    = FSPICS0_OUT_IDX,
+  .mosi_insig   = FSPID_IN_IDX,
+  .mosi_outsig  = FSPID_OUT_IDX,
+  .miso_insig   = FSPIQ_IN_IDX,
+  .miso_outsig  = FSPIQ_OUT_IDX,
+  .clk_insig    = FSPICLK_IN_IDX,
+  .clk_outsig   = FSPICLK_OUT_IDX
+};
+
+static const struct spi_ops_s esp32s2_spi2_ops =
+{
+  .lock              = esp32s2_spi_lock,
+#ifdef CONFIG_ESP32S2_SPI_UDCS
+  .select            = esp32s2_spi2_select,
+#else
+  .select            = esp32s2_spi_select,
+#endif
+  .setfrequency      = esp32s2_spi_setfrequency,
+  .setmode           = esp32s2_spi_setmode,
+  .setbits           = esp32s2_spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = esp32s2_spi_hwfeatures,
+#endif
+  .status            = esp32s2_spi2_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = esp32s2_spi2_cmddata,
+#endif
+  .send              = esp32s2_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = esp32s2_spi_exchange,
+#else
+  .sndblock          = esp32s2_spi_sndblock,
+  .recvblock         = esp32s2_spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = esp32s2_spi_trigger,
+#endif
+  .registercallback  = NULL,
+};
+
+static struct esp32s2_spi_priv_s esp32s2_spi2_priv =
+{
+  .spi_dev     =
+                {
+                  .ops = &esp32s2_spi2_ops
+                },
+  .config      = &esp32s2_spi2_config,
+  .refs        = 0,
+  .exclsem     = SEM_INITIALIZER(0),
+  .frequency   = 0,
+  .actual      = 0,
+  .mode        = 0,
+  .nbits       = 0
+};
+#endif /* CONFIG_ESP32S2_SPI2 */
+
+#ifdef CONFIG_ESP32S2_SPI3
+static const struct esp32s2_spi_config_s esp32s2_spi3_config =
+{
+  .clk_freq     = SPI_DEFAULT_FREQ,
+  .width        = SPI_DEFAULT_WIDTH,
+  .id           = 3,
+  .mode         = SPI_DEFAULT_MODE,
+  .cs_pin       = CONFIG_ESP32S2_SPI3_CSPIN,
+  .mosi_pin     = CONFIG_ESP32S2_SPI3_MOSIPIN,
+  .miso_pin     = CONFIG_ESP32S2_SPI3_MISOPIN,
+  .clk_pin      = CONFIG_ESP32S2_SPI3_CLKPIN,
+  .clk_bit      = SYSTEM_SPI3_CLK_EN,
+  .rst_bit      = SYSTEM_SPI3_RST,
+  .cs_insig     = FSPICS0_IN_IDX,
+  .cs_outsig    = FSPICS0_OUT_IDX,
+  .mosi_insig   = FSPID_IN_IDX,
+  .mosi_outsig  = FSPID_OUT_IDX,
+  .miso_insig   = FSPIQ_IN_IDX,
+  .miso_outsig  = FSPIQ_OUT_IDX,
+  .clk_insig    = FSPICLK_IN_IDX,
+  .clk_outsig   = FSPICLK_OUT_IDX
+};
+
+static const struct spi_ops_s esp32s2_spi3_ops =
+{
+  .lock              = esp32s2_spi_lock,
+#ifdef CONFIG_ESP32S2_SPI_UDCS
+  .select            = esp32s2_spi3_select,
+#else
+  .select            = esp32s2_spi_select,
+#endif
+  .setfrequency      = esp32s2_spi_setfrequency,
+  .setmode           = esp32s2_spi_setmode,
+  .setbits           = esp32s2_spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = esp32s2_spi_hwfeatures,
+#endif
+  .status            = esp32s2_spi3_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = esp32s2_spi3_cmddata,
+#endif
+  .send              = esp32s2_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = esp32s2_spi_exchange,
+#else
+  .sndblock          = esp32s2_spi_sndblock,
+  .recvblock         = esp32s2_spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = esp32s2_spi_trigger,
+#endif
+  .registercallback  = NULL,
+};
+
+static struct esp32s2_spi_priv_s esp32s2_spi3_priv =
+{
+  .spi_dev     =
+                {
+                  .ops = &esp32s2_spi3_ops
+                },
+  .config      = &esp32s2_spi3_config,
+  .refs        = 0,
+  .exclsem     = SEM_INITIALIZER(0),
+  .frequency   = 0,
+  .actual      = 0,
+  .mode        = 0,
+  .nbits       = 0
+};
+#endif /* CONFIG_ESP32S2_SPI3 */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32s2_spi_set_regbits
+ *
+ * Description:
+ *   Set the bits of the SPI register.
+ *
+ * Input Parameters:
+ *   addr   - Address of the register of interest
+ *   bits   - Bits to be set
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static inline void esp32s2_spi_set_regbits(uint32_t addr, uint32_t bits)
+{
+  uint32_t tmp = getreg32(addr);
+
+  putreg32(tmp | bits, addr);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_clr_regbits
+ *
+ * Description:
+ *   Clear the bits of the SPI register.
+ *
+ * Input Parameters:
+ *   addr   - Address of the register of interest
+ *   bits   - Bits to be cleared
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static inline void esp32s2_spi_clr_regbits(uint32_t addr, uint32_t bits)
+{
+  uint32_t tmp = getreg32(addr);
+
+  putreg32(tmp & ~bits, addr);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_iomux
+ *
+ * Description:
+ *   Check if the option SPI GPIO pins can use IOMUX directly
+ *
+ * Input Parameters:
+ *   priv   - Private SPI device structure
+ *
+ * Returned Value:
+ *   True if can use IOMUX or false if can't.
+ *
+ ****************************************************************************/
+
+static inline bool esp32s2_spi_iomux(struct esp32s2_spi_priv_s *priv)
+{
+  bool mapped = false;
+  const struct esp32s2_spi_config_s *cfg = priv->config;
+
+  /* We only need to check SPI2, SPI3 doesn't support IOMUX */
+
+  if (cfg->id == 2)
+    {
+      if (cfg->mosi_pin == SPI2_IOMUX_MOSIPIN &&
+#ifndef CONFIG_ESP32S2_SPI_SWCS
+          cfg->cs_pin == SPI2_IOMUX_CSPIN &&
+#endif
+          cfg->miso_pin == SPI2_IOMUX_MISOPIN &&
+          cfg->clk_pin == SPI2_IOMUX_CLKPIN)
+        {
+          mapped = true;
+        }
+    }
+
+  return mapped;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_lock
+ *
+ * Description:
+ *   Lock or unlock the SPI device.
+ *
+ * Input Parameters:
+ *   dev    - Device-specific state data
+ *   lock   - true: Lock SPI bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   The result of lock or unlock the SPI device.
+ *
+ ****************************************************************************/
+
+static int esp32s2_spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  int ret;
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_select
+ *
+ * Description:
+ *   Enable/disable the SPI chip select. The implementation of this method
+ *   must include handshaking: If a device is selected, it must hold off
+ *   all other attempts to select the device until the device is deselected.
+ *
+ *   If ESP32S2_SPI_SWCS is disabled, the driver will use hardware CS so that
+ *   once transmission is started the hardware selects the device and when
+ *   this transmission is done hardware deselects the device automatically.
+ *   So, this function will do nothing.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   devid    - Identifies the device to select
+ *   selected - true: slave selected, false: slave de-selected
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_ESP32S2_SPI_UDCS
+static void esp32s2_spi_select(struct spi_dev_s *dev,
+                               uint32_t devid, bool selected)
+{
+#if SPI_HAVE_SWCS
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  esp32s2_gpiowrite(priv->config->cs_pin, !selected);
+#endif
+
+  spiinfo("devid: %08" PRIx32 " CS: %s\n",
+          devid, selected ? "select" : "free");
+}
+#endif
+
+/****************************************************************************
+ * Name: esp32s2_spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev       - Device-specific state data
+ *   frequency - The requested SPI frequency
+ *
+ * Returned Value:
+ *   Returns the current selected frequency.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_setfrequency(struct spi_dev_s *dev,
+                                         uint32_t frequency)
+{
+  uint32_t reg_val;
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+  const uint32_t duty_cycle = 128;
+
+  if (priv->frequency == frequency)
+    {
+      /* Requested frequency is the same as the current frequency. */
+
+      return priv->actual;
+    }
+
+  /* In HW, n, h and l fields range from 1 to 64, pre ranges from 1 to 8K.
+   * The value written to register is one lower than the used value.
+   */
+
+  if (frequency > ((APB_CLK_FREQ / 4) * 3))
+    {
+      /* Using APB frequency directly will give us the best result here. */
+
+      reg_val = SPI_CLK_EQU_SYSCLK_M;
+      priv->actual = APB_CLK_FREQ;
+    }
+  else
+    {
+      /* For best duty cycle resolution, we want n to be as close to 32 as
+       * possible, but we also need a pre/n combo that gets us as close as
+       * possible to the intended frequency. To do this, we bruteforce n and
+       * calculate the best pre to go along with that. If there's a choice
+       * between pre/n combos that give the same result, use the one with the
+       * higher n.
+       */
+
+      int32_t pre;
+      int32_t n;
+      int32_t h;
+      int32_t l;
+      int32_t bestn = -1;
+      int32_t bestpre = -1;
+      int32_t besterr = 0;
+      int32_t errval;
+
+      /* Start at n = 2. We need to be able to set h/l so we have at least
+       * one high and one low pulse.
+       */
+
+      for (n = 2; n <= 64; n++)
+        {
+          /* Effectively, this does:
+           *   pre = round((APB_CLK_FREQ / n) / frequency)
+           */
+
+          pre = ((APB_CLK_FREQ / n) + (frequency / 2)) / frequency;
+
+          if (pre <= 0)
+            {
+              pre = 1;
+            }
+
+          if (pre > 16)
+            {
+              pre = 16;
+            }
+
+          errval = abs(APB_CLK_FREQ / (pre * n) - frequency);
+          if (bestn == -1 || errval <= besterr)
+            {
+              besterr = errval;
+              bestn = n;
+              bestpre = pre;
+            }
+        }
+
+      n = bestn;
+      pre = bestpre;
+      l = n;
+
+      /* Effectively, this does:
+       *   h = round((duty_cycle * n) / 256)
+       */
+
+      h = (duty_cycle * n + 127) / 256;
+      if (h <= 0)
+        {
+          h = 1;
+        }
+
+      reg_val = ((l - 1) << SPI_CLKCNT_L_S) |
+                ((h - 1) << SPI_CLKCNT_H_S) |
+                ((n - 1) << SPI_CLKCNT_N_S) |
+                ((pre - 1) << SPI_CLKDIV_PRE_S);
+
+      priv->actual = APB_CLK_FREQ / (n * pre);
+    }
+
+  priv->frequency = frequency;
+
+  putreg32(reg_val, SPI_CLOCK_REG(priv->config->id));
+
+  spiinfo("frequency=%" PRIu32 ", actual=%" PRIu32 "\n",
+          priv->frequency, priv->actual);
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The requested SPI mode
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_setmode(struct spi_dev_s *dev,
+                                enum spi_mode_e mode)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      uint32_t ck_idle_edge;
+      uint32_t ck_out_edge;
+
+      switch (mode)
+        {
+          case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+            ck_idle_edge = 0;
+            ck_out_edge = 0;
+            break;
+
+          case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+            ck_idle_edge = 0;
+            ck_out_edge = 1;
+            break;
+
+          case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+            ck_idle_edge = 1;
+            ck_out_edge = 1;
+            break;
+
+          case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+            ck_idle_edge = 1;
+            ck_out_edge = 0;
+            break;
+
+          default:
+            spierr("Invalid mode: %d\n", mode);
+            DEBUGPANIC();
+            return;
+        }
+
+      esp32s2_spi_clr_regbits(SPI_MISC_REG(priv->config->id),
+                              SPI_CK_IDLE_EDGE_M);
+      esp32s2_spi_set_regbits(SPI_MISC_REG(priv->config->id),
+                              VALUE_MASK(ck_idle_edge, SPI_CK_IDLE_EDGE));
+
+      esp32s2_spi_clr_regbits(SPI_USER_REG(priv->config->id),
+                              SPI_CK_OUT_EDGE_M);
+      esp32s2_spi_set_regbits(SPI_USER_REG(priv->config->id),
+                              VALUE_MASK(ck_out_edge, SPI_CK_OUT_EDGE));
+
+      priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_setbits
+ *
+ * Description:
+ *   Set the number of bits per word.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *   nbits - The number of bits in an SPI word.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  spiinfo("nbits=%d\n", nbits);
+
+  priv->nbits = nbits;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_hwfeatures
+ *
+ * Description:
+ *   Set hardware-specific feature flags.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   features - H/W feature flags
+ *
+ * Returned Value:
+ *   Zero (OK) if the selected H/W features are enabled; A negated errno
+ *   value if any H/W feature is not supportable.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_HWFEATURES
+static int esp32s2_spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features)
+{
+  /* Other H/W features are not supported */
+
+  return (features == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp32s2_spi_poll_send
+ *
+ * Description:
+ *   Send one word on SPI by polling mode.
+ *
+ * Input Parameters:
+ *   priv - SPI private state data
+ *   wd   - The word to send. The size of the data is determined by the
+ *          number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   Received value.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_poll_send(struct esp32s2_spi_priv_s *priv,
+                                      uint32_t wd)
+{
+  uint32_t val;
+
+  const uintptr_t spi_miso_dlen_reg = SPI_MISO_DLEN_REG(priv->config->id);
+  const uintptr_t spi_mosi_dlen_reg = SPI_MOSI_DLEN_REG(priv->config->id);
+  const uintptr_t spi_w0_reg = SPI_W0_REG(priv->config->id);
+  const uintptr_t spi_cmd_reg = SPI_CMD_REG(priv->config->id);
+
+  putreg32((priv->nbits - 1), spi_miso_dlen_reg);
+  putreg32((priv->nbits - 1), spi_mosi_dlen_reg);
+
+  putreg32(wd, spi_w0_reg);
+
+  esp32s2_spi_set_regbits(spi_cmd_reg, SPI_USR_M);
+
+  while ((getreg32(spi_cmd_reg) & SPI_USR_M) != 0)
+    {
+      ;
+    }
+
+  val = getreg32(spi_w0_reg);
+
+  spiinfo("send=0x%" PRIx32 " and recv=0x%" PRIx32 "\n", wd, val);
+
+  return val;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_send
+ *
+ * Description:
+ *   Send one word on SPI.
+ *
+ * Input Parameters:
+ *   dev - Device-specific state data
+ *   wd  - The word to send. The size of the data is determined by the
+ *         number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   Received value.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32s2_spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  return esp32s2_spi_poll_send(priv, wd);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_poll_exchange
+ *
+ * Description:
+ *   Exchange a block of data from SPI.
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_poll_exchange(struct esp32s2_spi_priv_s *priv,
+                                      const void *txbuffer,
+                                      void *rxbuffer,
+                                      size_t nwords)
+{
+  const uint32_t total_bytes = nwords * (priv->nbits / 8);
+  uintptr_t bytes_remaining = total_bytes;
+  const uint8_t *tp = (const uint8_t *)txbuffer;
+  uint8_t *rp = (uint8_t *)rxbuffer;
+
+  while (bytes_remaining != 0)
+    {
+      /* Initialize data_buf_reg with the address of the first data buffer
+       * register (W0).
+       */
+
+      uintptr_t data_buf_reg = SPI_W0_REG(priv->config->id);
+      uint32_t transfer_size = MIN(SPI_MAX_BUF_SIZE, bytes_remaining);
+
+      /* Write data words to data buffer registers.
+       * SPI peripheral contains 16 registers (W0 - W15).
+       */
+
+      for (int i = 0 ; i < transfer_size; i += sizeof(uintptr_t))
+        {
+          uint32_t w_wd = UINT32_MAX;
+
+          if (tp != NULL)
+            {
+              memcpy(&w_wd, tp, sizeof(uintptr_t));
+
+              tp += sizeof(uintptr_t);
+            }
+
+          putreg32(w_wd, data_buf_reg);
+
+          spiinfo("send=0x%" PRIx32 " data_reg=0x%" PRIxPTR "\n",
+                  w_wd, data_buf_reg);
+
+          /* Update data_buf_reg to point to the next data buffer register. */
+
+          data_buf_reg += sizeof(uintptr_t);
+        }
+
+      esp32s2_spi_set_regbits(SPI_USER_REG(priv->config->id),
+                              SPI_USR_MOSI_M);
+
+      if (rp == NULL)
+        {
+          esp32s2_spi_clr_regbits(SPI_USER_REG(priv->config->id),
+                                  SPI_USR_MISO_M);
+        }
+      else
+        {
+          esp32s2_spi_set_regbits(SPI_USER_REG(priv->config->id),
+                                  SPI_USR_MISO_M);
+        }
+
+      putreg32((transfer_size * 8) - 1,
+               SPI_MOSI_DLEN_REG(priv->config->id));
+
+      putreg32((transfer_size * 8) - 1,
+               SPI_MISO_DLEN_REG(priv->config->id));
+
+      /* Trigger start of user-defined transaction for master. */
+
+      esp32s2_spi_set_regbits(SPI_CMD_REG(priv->config->id), SPI_USR_M);
+
+      /* Wait for the user-defined transaction to finish. */
+
+      while ((getreg32(SPI_CMD_REG(priv->config->id)) & SPI_USR_M) != 0)
+        {
+          ;
+        }
+
+      if (rp != NULL)
+        {
+          /* Set data_buf_reg with the address of the first data buffer
+           * register (W0).
+           */
+
+          data_buf_reg = SPI_W0_REG(priv->config->id);
+
+          /* Read received data words from SPI data buffer registers. */
+
+          for (int i = 0 ; i < transfer_size; i += sizeof(uintptr_t))
+            {
+              uint32_t r_wd = getreg32(data_buf_reg);
+
+              spiinfo("recv=0x%" PRIx32 " data_reg=0x%" PRIxPTR "\n",
+                      r_wd, data_buf_reg);
+
+              memcpy(rp, &r_wd, sizeof(uintptr_t));
+
+              rp += sizeof(uintptr_t);
+
+              /* Update data_buf_reg to point to the next data buffer
+               * register.
+               */
+
+              data_buf_reg += sizeof(uintptr_t);
+            }
+        }
+
+      bytes_remaining -= transfer_size;
+    }
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_exchange
+ *
+ * Description:
+ *   Exchange a block of data from SPI.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific 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.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_exchange(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 void *rxbuffer,
+                                 size_t nwords)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  esp32s2_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords);
+}
+
+#ifndef CONFIG_SPI_EXCHANGE
+
+/****************************************************************************
+ * Name: esp32s2_spi_sndblock
+ *
+ * Description:
+ *   Send a block of data on SPI.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   nwords   - The length of data to send from the buffer in number 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.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_sndblock(struct spi_dev_s *dev,
+                                 const void *txbuffer,
+                                 size_t nwords)
+{
+  spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords);
+
+  esp32s2_spi_exchange(dev, txbuffer, NULL, nwords);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_recvblock
+ *
+ * Description:
+ *   Receive a block of data from SPI.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   rxbuffer - A pointer to the buffer in which to receive data
+ *   nwords   - The length of data that can be received in the buffer in
+ *              number 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.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_recvblock(struct spi_dev_s *dev,
+                                  void *rxbuffer,
+                                  size_t nwords)
+{
+  spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords);
+
+  esp32s2_spi_exchange(dev, NULL, rxbuffer, nwords);
+}
+#endif
+
+/****************************************************************************
+ * Name: esp32s2_spi_init
+ *
+ * Description:
+ *   Initialize ESP32-S2 SPI hardware interface.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_init(struct spi_dev_s *dev)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+  const struct esp32s2_spi_config_s *config = priv->config;
+  uint32_t regval;
+
+  /* Initialize the SPI semaphore that enforces mutually exclusive access */
+
+  nxsem_init(&priv->exclsem, 0, 1);
+
+  esp32s2_gpiowrite(config->cs_pin, true);
+  esp32s2_gpiowrite(config->mosi_pin, true);
+  esp32s2_gpiowrite(config->miso_pin, true);
+  esp32s2_gpiowrite(config->clk_pin, true);
+
+#if SPI_HAVE_SWCS
+  esp32s2_configgpio(config->cs_pin, OUTPUT_FUNCTION_1);
+  esp32s2_gpio_matrix_out(config->cs_pin, SIG_GPIO_OUT_IDX, 0, 0);
+#endif
+
+  /* SPI3 doesn't have IOMUX, if SPI3 is enabled use GPIO Matrix for both */
+
+  if (esp32s2_spi_iomux(priv))
+    {
+#if !SPI_HAVE_SWCS
+      esp32s2_configgpio(config->cs_pin, OUTPUT_FUNCTION_5);
+      esp32s2_gpio_matrix_out(config->cs_pin, SIG_GPIO_OUT_IDX, 0, 0);
+#endif
+      esp32s2_configgpio(config->mosi_pin, OUTPUT_FUNCTION_5);
+      esp32s2_gpio_matrix_out(config->mosi_pin, SIG_GPIO_OUT_IDX, 0, 0);
+
+      esp32s2_configgpio(config->miso_pin, INPUT_FUNCTION_5 | PULLUP);
+      esp32s2_gpio_matrix_out(config->miso_pin, SIG_GPIO_OUT_IDX, 0, 0);
+
+      esp32s2_configgpio(config->clk_pin, OUTPUT_FUNCTION_5);
+      esp32s2_gpio_matrix_out(config->clk_pin, SIG_GPIO_OUT_IDX, 0, 0);
+    }
+  else
+    {
+#if !SPI_HAVE_SWCS
+      esp32s2_configgpio(config->cs_pin, OUTPUT);
+      esp32s2_gpio_matrix_out(config->cs_pin, config->cs_outsig, 0, 0);
+#endif
+      esp32s2_configgpio(config->mosi_pin, OUTPUT);
+      esp32s2_gpio_matrix_out(config->mosi_pin, config->mosi_outsig, 0, 0);
+
+      esp32s2_configgpio(config->miso_pin, INPUT | PULLUP);
+      esp32s2_gpio_matrix_in(config->miso_pin, config->miso_insig, 0);
+
+      esp32s2_configgpio(config->clk_pin, OUTPUT);
+      esp32s2_gpio_matrix_out(config->clk_pin, config->clk_outsig, 0, 0);
+    }
+
+  modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, 0, config->clk_bit);
+  modifyreg32(SYSTEM_PERIP_RST_EN0_REG, config->rst_bit, 0);
+
+  regval = SPI_DOUTDIN_M | SPI_USR_MISO_M | SPI_USR_MOSI_M | SPI_CS_HOLD_M;
+  putreg32(regval, SPI_USER_REG(priv->config->id));
+  putreg32(0, SPI_USER1_REG(priv->config->id));
+  putreg32(0, SPI_SLAVE_REG(priv->config->id));
+  putreg32(SPI_CS1_DIS_M | SPI_CS2_DIS_M,
+           SPI_MISC_REG(priv->config->id));
+
+#if SPI_HAVE_SWCS
+  esp32s2_spi_set_regbits(SPI_MISC_REG(priv->config->id), SPI_CS0_DIS_M);
+#endif
+
+  putreg32(0, SPI_CTRL_REG(priv->config->id));
+  putreg32(VALUE_MASK(0, SPI_CS_HOLD_TIME),
+           SPI_USER1_REG(priv->config->id));
+
+  esp32s2_spi_setfrequency(dev, config->clk_freq);
+  esp32s2_spi_setbits(dev, config->width);
+  esp32s2_spi_setmode(dev, config->mode);
+}
+
+/****************************************************************************
+ * Name: esp32s2_spi_deinit
+ *
+ * Description:
+ *   Deinitialize ESP32-S2 SPI hardware interface.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void esp32s2_spi_deinit(struct spi_dev_s *dev)
+{
+  struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev;
+
+  modifyreg32(SYSTEM_PERIP_RST_EN0_REG, 0, priv->config->clk_bit);
+  modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, priv->config->clk_bit, 0);
+
+  priv->frequency = 0;
+  priv->actual    = 0;
+  priv->mode      = SPIDEV_MODE0;
+  priv->nbits     = 0;
+}
+
+/****************************************************************************
+ * Name: esp32s2_spibus_initialize
+ *
+ * Description:
+ *   Initialize the selected SPI bus.
+ *
+ * Input Parameters:
+ *   port     - Port number (for hardware that has multiple SPI interfaces)
+ *
+ * Returned Value:
+ *   Valid SPI device structure reference on success; NULL on failure.
+ *
+ ****************************************************************************/
+
+struct spi_dev_s *esp32s2_spibus_initialize(int port)
+{
+  struct spi_dev_s *spi_dev;
+  struct esp32s2_spi_priv_s *priv;
+  irqstate_t flags;
+
+  switch (port)
+    {
+#ifdef CONFIG_ESP32S2_SPI2
+      case ESP32S2_SPI2:
+        priv = &esp32s2_spi2_priv;
+        break;
+#endif
+#ifdef CONFIG_ESP32S2_SPI3
+      case ESP32S2_SPI3:
+        priv = &esp32s2_spi3_priv;
+        break;
+#endif
+      default:
+        return NULL;
+    }
+
+  spi_dev = (struct spi_dev_s *)priv;
+
+  flags = spin_lock_irqsave(&priv->lock);

Review Comment:
   ```suggestion
     flags = enter_critical_section();
   ```
   Since SMP is not supported on ESP32-S2, use the `[enter|leave]_critical_section` API for defining critical sections.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] gustavonihei commented on a diff in pull request #6823: xtensa/esp32s2: Add basic support to SPI

Posted by GitBox <gi...@apache.org>.
gustavonihei commented on code in PR #6823:
URL: https://github.com/apache/incubator-nuttx/pull/6823#discussion_r942389929


##########
arch/xtensa/src/esp32s2/esp32s2_spi.c:
##########
@@ -0,0 +1,1231 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32s2/esp32s2_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_ESP32S2_SPI
+
+#include <assert.h>
+#include <debug.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/spi/spi.h>
+
+#include <arch/board/board.h>
+
+#include "esp32s2_spi.h"
+#include "esp32s2_irq.h"
+#include "esp32s2_gpio.h"
+
+#include "xtensa.h"
+#include "hardware/esp32s2_gpio_sigmap.h"
+#include "hardware/esp32s2_pinmap.h"
+#include "hardware/esp32s2_spi.h"
+#include "hardware/esp32s2_soc.h"
+#include "hardware/esp32s2_system.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Check if Chip-Select pin will be controlled via software */
+
+#ifdef CONFIG_ESP32S2_SPI_SWCS
+#  define SPI_HAVE_SWCS 1
+#else
+#  define SPI_HAVE_SWCS 0
+#endif
+
+/* SPI default frequency (limited by clock divider) */
+
+#define SPI_DEFAULT_FREQ  (400000)
+
+/* SPI default width */
+
+#define SPI_DEFAULT_WIDTH (8)
+
+/* SPI default mode */
+
+#define SPI_DEFAULT_MODE  (SPIDEV_MODE0)
+
+/* Helper for applying the mask for a given register field.
+ * Mask is determined by the macros suffixed with _V and _S from the
+ * peripheral register description.
+ */
+
+#define VALUE_MASK(_val, _field) (((_val) & (_field##_V)) << (_field##_S))
+
+/* SPI Maximum buffer size in bytes */
+
+#define SPI_MAX_BUF_SIZE (64)
+
+#ifndef MIN
+#  define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* SPI Device hardware configuration */
+
+struct esp32s2_spi_config_s
+{
+  uint32_t clk_freq;          /* SPI default clock frequency */
+  uint32_t width;             /* SPI default width */
+  enum spi_mode_e mode;       /* SPI default mode */
+
+  uint8_t id;                 /* ESP32-S2 SPI device ID: SPIx {2,3} */
+  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 clk_bit;           /* Clock enable bit */
+  uint32_t rst_bit;           /* SPI reset bit */
+  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 */
+};
+
+struct esp32s2_spi_priv_s
+{
+  /* Externally visible part of the SPI interface */
+
+  struct spi_dev_s spi_dev;
+
+  /* Port configuration */
+
+  const struct esp32s2_spi_config_s *config;
+  int refs;             /* Reference count */
+  sem_t exclsem;        /* Held while chip is selected for mutual exclusion */
+  uint32_t frequency;   /* Requested clock frequency */
+  uint32_t actual;      /* Actual clock frequency */
+  enum spi_mode_e mode; /* Actual SPI hardware mode */
+  uint8_t nbits;        /* Actual SPI send/receive bits once transmission */
+  spinlock_t lock;      /* Device specific lock. */

Review Comment:
   ```suggestion
   ```
   Device-specific lock is not required for ESP32-S2, since it does not support SMP.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org