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/06/22 17:32:51 UTC

[GitHub] [incubator-nuttx] adamkaliszan commented on a diff in pull request #6492: Stm32wl55JC SPI driver

adamkaliszan commented on code in PR #6492:
URL: https://github.com/apache/incubator-nuttx/pull/6492#discussion_r904057914


##########
arch/arm/src/stm32wl5/stm32wl5_spi.c:
##########
@@ -0,0 +1,1858 @@
+/****************************************************************************
+ * arch/arm/src/stm32wl5/stm32wl5_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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wl5_spi1/2select and stm32wl5_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).
+ * All other methods (including stm32wl5_spibus_initialize())  are provided
+ * by common STM32 logic.  To use this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wl5_boardinitialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wl5_spi1/2select() and stm32wl5_spi1/2() functions
+ *      in your board-specific logic.  These functions will perform chip
+ *      selection and status operations using GPIOs in the way your board is
+ *      configured.
+ *   3. Add a calls to stm32wl5_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wl5_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wl5.h"
+#include "stm32wl5_gpio.h"
+#ifdef CONFIG_STM32WL5_SPI_DMA
+#include "stm32wl5_dma.h"
+#endif
+#include "stm32wl5_spi.h"
+
+#if defined(CONFIG_STM32WL5_SPI1) || defined(CONFIG_STM32WL5_SPI2S2)
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WL5_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+#  error "DMA driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WL5_SPI_INTERRUPTS) && defined(CONFIG_STM32WL5_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  elif defined(CONFIG_STM32WL5_STM32WL5XXX_CPU1
+#warning "Verify, read doc and Implement"
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  else
+#    error "Unknown STM32WL5 DMA"
+#  endif
+
+#  if defined(CONFIG_STM32WL5_STM32WL5XXX_CPU1)
+#    if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#      error "Illegal value for CONFIG_SPI_DMAPRIO"
+#    endif
+#  else
+#    error "Unknown STM32 DMA"
+#  endif
+
+/* DMA channel configuration */
+
+#if defined(CONFIG_STM32WL5_STM32WL5XXX_CPU1)
+#  define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#  define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#  define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#  define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#  define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#  define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#  define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#  define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+#else
+#  error "Unknown STM32WL5 DMA"
+#endif
+
+#  define SPIDMA_BUFFER_MASK   (4 - 1)
+#  define SPIDMA_SIZE(b) (((b) + SPIDMA_BUFFER_MASK) & ~SPIDMA_BUFFER_MASK)
+#  define SPIDMA_BUF_ALIGN   aligned_data(4)
+
+#  if defined(CONFIG_STM32WL5_SPI1_DMA_BUFFER) && \
+            CONFIG_STM32WL5_SPI1_DMA_BUFFER > 0
+#    define SPI1_DMABUFSIZE_ADJUSTED SPIDMA_SIZE(CONFIG_STM32WL5_SPI1_DMA_BUFFER)
+#    define SPI1_DMABUFSIZE_ALGN SPIDMA_BUF_ALIGN
+#  endif
+
+#  if defined(CONFIG_STM32WL5_SPI2S2_DMA_BUFFER) && \
+            CONFIG_STM32WL5_SPI2S2_DMA_BUFFER > 0
+#    define SPI2S2_DMABUFSIZE_ADJUSTED SPIDMA_SIZE(CONFIG_STM32WL5_SPI2S2_DMA_BUFFER)
+#    define SPI2S2_DMABUFSIZE_ALGN SPIDMA_BUF_ALIGN
+#  endif
+
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wl5_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WL5_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WL5_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint8_t          rxch;         /* The RX DMA channel number */
+  uint8_t          txch;         /* The TX DMA channel number */
+  uint8_t          *rxbuf;       /* The RX DMA buffer */
+  uint8_t          *txbuf;       /* The TX DMA buffer */
+  size_t           buflen;       /* The DMA buffer length */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wl5_spidev_s *priv,
+                                  uint8_t offset);
+
+static inline uint8_t spi_getreg8(struct stm32wl5_spidev_s *priv,
+                                  uint8_t offset);
+
+static inline void spi_putreg(struct stm32wl5_spidev_s *priv,
+                              uint8_t offset,
+                              uint16_t value);
+
+static inline void spi_putreg8(struct stm32wl5_spidev_s *priv,
+                              uint8_t offset,
+                              uint8_t value);
+
+static inline uint16_t spi_readword(struct stm32wl5_spidev_s *priv);
+static inline void spi_writeword(struct stm32wl5_spidev_s *priv,
+                                 uint16_t byte);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static int         spi_dmarxwait(struct stm32wl5_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wl5_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wl5_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wl5_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wl5_spidev_s *priv,
+                                  void *rxbuffer,
+                                  void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wl5_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy,
+                                  size_t nwords);
+static inline void spi_dmarxstart(struct stm32wl5_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wl5_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer,
+                                 size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wl5_spidev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI1
+static const struct spi_ops_s g_sp1iops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wl5_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wl5_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wl5_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wl5_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+#if defined(SPI1_DMABUFSIZE_ADJUSTED)
+static uint8_t g_spi1_txbuf[SPI1_DMABUFSIZE_ADJUSTED] SPI1_DMABUFSIZE_ALGN;
+static uint8_t g_spi1_rxbuf[SPI1_DMABUFSIZE_ADJUSTED] SPI1_DMABUFSIZE_ALGN;
+#endif
+
+static struct stm32wl5_spidev_s g_spi1dev =
+{
+  .spidev   =
+              {
+               &g_sp1iops
+              },
+  .spibase  = STM32WL5_SPI1_BASE,
+  .spiclock = STM32WL5_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WL5_SPI_INTERRUPTS
+  .spiirq   = STM32WL5_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WL5_SPI_DMA
+#  ifdef CONFIG_STM32WL5_SPI1_DMA
+  .rxch     = DMACHAN_SPI1_RX,
+  .txch     = DMACHAN_SPI1_TX,
+#if defined(SPI1_DMABUFSIZE_ADJUSTED)
+  .rxbuf    = g_spi1_rxbuf,
+  .txbuf    = g_spi1_txbuf,
+  .buflen   = SPI1_DMABUFSIZE_ADJUSTED,
+#    endif
+#  else
+  .rxch     = 0,
+  .txch     = 0,
+#  endif
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WL5_SPI2S2
+static const struct spi_ops_s g_sp2iops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wl5_spi2s2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wl5_spi2s2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wl5_spi2s2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wl5_s2register,  /* provided externally */
+#else
+  .registercallback  = 0,                   /* not implemented */
+#endif
+};
+
+#if defined(SPI2S2_DMABUFSIZE_ADJUSTED)
+static uint8_t g_spi2s2_txbuf[SPI2S2_DMABUFSIZE_ADJUSTED]
+  SPI2S2_DMABUFSIZE_ALGN;
+static uint8_t g_spi2s2_rxbuf[SPI2S2_DMABUFSIZE_ADJUSTED]
+  SPI2S2_DMABUFSIZE_ALGN;
+#endif
+
+static struct stm32wl5_spidev_s g_spi2s2dev =
+{
+  .spidev   =
+              {
+               &g_sp2iops
+              },
+  .spibase  = STM32WL5_SPI2S2_BASE,
+  .spiclock = STM32WL5_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WL5_SPI_INTERRUPTS
+  .spiirq   = STM32WL5_IRQ_SPI2S2,
+#endif
+#ifdef CONFIG_STM32WL5_SPI_DMA
+#  ifdef CONFIG_STM32WL5_SPI2S2_DMA
+  .rxch     = DMACHAN_SPI2S2_RX,
+  .txch     = DMACHAN_SPI2S2_TX,
+#if defined(SPI2S2_DMABUFSIZE_ADJUSTED)
+  .rxbuf    = g_spi2s2_rxbuf,
+  .txbuf    = g_spi2s2_txbuf,
+  .buflen   = SPI2S2_DMABUFSIZE_ADJUSTED,
+#    endif
+#  else
+  .rxch     = 0,
+  .txch     = 0,
+#  endif
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wl5_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wl5_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wl5_spidev_s *priv,
+                              uint8_t offset,
+                              uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write an 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wl5_spidev_s *priv,
+                               uint8_t offset,
+                               uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wl5_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WL5_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0)
+    {
+    }
+
+  /* Then return the received byte */
+
+  /* "When the data frame size fits into one byte
+   * (less than or equal to 8 bits),
+   *  data packing is used automatically when any read or write 16-bit access
+   *  is performed on the SPIx_DR register. The double data frame pattern is
+   *  handled in parallel in this case. At first, the SPI operates using the
+   *  pattern stored in the LSB of the accessed word, then with the other
+   *  half stored in the MSB.... The receiver then has to access both data
+   *  frames by a single 16-bit read of SPIx_DR as a response to this single
+   *  RXNE event. The RxFIFO threshold setting and the following read access
+   *  must be always kept aligned at the receiver side, as data can be lost
+   *  if it is not in line."
+   */
+
+  if (priv->nbits < 9)
+    {
+      return (uint16_t)spi_getreg8(priv, STM32WL5_SPI_DR_OFFSET);
+    }
+  else
+    {
+      return spi_getreg(priv, STM32WL5_SPI_DR_OFFSET);
+    }
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one word or byte to SPI. If the frame size is 8 bit or lower
+ *   a byte is written. In other case a word is written.
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   word - word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wl5_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WL5_SPI_SR_OFFSET) & SPI_SR_TXE) == 0)
+    {
+    }
+
+  /* Then send the word */
+
+  if (priv->nbits < 9)
+    {
+      spi_putreg8(priv, STM32WL5_SPI_DR_OFFSET, (uint8_t)word);
+    }
+  else
+    {
+      spi_putreg(priv, STM32WL5_SPI_DR_OFFSET, word);
+    }
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static int spi_dmarxwait(struct stm32wl5_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static int spi_dmatxwait(struct stm32wl5_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->txsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->txresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static inline void spi_dmarxwakeup(struct stm32wl5_spidev_s *priv)
+{
+  nxsem_post(&priv->rxsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static inline void spi_dmatxwakeup(struct stm32wl5_spidev_s *priv)
+{
+  nxsem_post(&priv->txsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->rxresult = isr | 0x080;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmarxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->txresult = isr | 0x080;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmatxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxsetup
+ *
+ * Description:
+ *   Setup to perform RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static void spi_dmarxsetup(struct stm32wl5_spidev_s *priv,
+                           void *rxbuffer,
+                           void *rxdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (priv->nbits > 8)
+    {
+      /* 16-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA16_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA8_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Configure the RX DMA */
+
+  stm32wl5_dmasetup(priv->rxdma, priv->spibase + STM32WL5_SPI_DR_OFFSET,
+                    (uint32_t)rxbuffer, nwords, priv->rxccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxsetup
+ *
+ * Description:
+ *   Setup to perform TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static void spi_dmatxsetup(struct stm32wl5_spidev_s *priv,
+                           const void *txbuffer,
+                           const void *txdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (priv->nbits > 8)
+    {
+      /* 16-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA16_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA8_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Setup the TX DMA */
+
+  stm32wl5_dmasetup(priv->txdma, priv->spibase + STM32WL5_SPI_DR_OFFSET,
+                    (uint32_t)txbuffer, nwords, priv->txccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxstart
+ *
+ * Description:
+ *   Start RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static inline void spi_dmarxstart(struct stm32wl5_spidev_s *priv)
+{
+  priv->rxresult = 0;
+  stm32wl5_dmastart(priv->rxdma, spi_dmarxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxstart
+ *
+ * Description:
+ *   Start TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static inline void spi_dmatxstart(struct stm32wl5_spidev_s *priv)
+{
+  priv->txresult = 0;
+  stm32wl5_dmastart(priv->txdma, spi_dmatxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_modifycr1
+ *
+ * Description:
+ *   Clear and set bits in the CR1 register
+ *
+ * Input Parameters:
+ *   priv    - Device-specific state data
+ *   clrbits - The bits to clear
+ *   setbits - The bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_modifycr1(struct stm32wl5_spidev_s *priv,
+                          uint16_t setbits,
+                          uint16_t clrbits)
+{
+  uint16_t cr1;
+  cr1 = spi_getreg(priv, STM32WL5_SPI_CR1_OFFSET);
+  cr1 &= ~clrbits;
+  cr1 |= setbits;
+  spi_putreg(priv, STM32WL5_SPI_CR1_OFFSET, cr1);
+
+  spiinfo("CR1 (0x%x) = 0x%04lx\n", priv->spibase + STM32WL5_SPI_CR1_OFFSET,
+           cr1);
+}
+
+/****************************************************************************
+ * Name: spi_modifycr2
+ *
+ * Description:
+ *   Clear and set bits in the CR2 register
+ *
+ * Input Parameters:
+ *   priv    - Device-specific state data
+ *   clrbits - The bits to clear
+ *   setbits - The bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_modifycr2(struct stm32wl5_spidev_s *priv, uint16_t setbits,
+                          uint16_t clrbits)
+{
+  uint16_t cr2;
+  cr2  = spi_getreg(priv, STM32WL5_SPI_CR2_OFFSET);
+  cr2 &= ~clrbits;
+  cr2 |= setbits;
+  spi_putreg(priv, STM32WL5_SPI_CR2_OFFSET, cr2);
+  spiinfo("CR2 = 0x%04x\n", cr2);
+}
+
+/****************************************************************************
+ * Name: spi_lock
+ *
+ * Description:
+ *   On SPI buses where there are multiple devices, it will be necessary to
+ *   lock SPI to have exclusive access to the buses for a sequence of
+ *   transfers.  The bus should be locked before the chip is selected. After
+ *   locking the SPI bus, the caller should then also call the setfrequency,
+ *   setbits, and setmode methods to make sure that the SPI is properly
+ *   configured for the device.  If the SPI bus is being shared, then it
+ *   may have been left in an incompatible state.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   lock - true: Lock spi bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  int ret;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev -       Device-specific state data
+ *   frequency - The SPI frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32_t spi_setfrequency(struct spi_dev_s *dev,
+                                 uint32_t frequency)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  uint16_t setbits;
+  uint32_t actual;
+
+  /* Has the frequency changed? */
+
+  if (frequency != priv->frequency)
+    {
+      /* Choices are limited by PCLK frequency with a set of divisors */
+
+      if (frequency >= priv->spiclock >> 1)
+        {
+          /* More than fPCLK/2.  This is as fast as we can go */
+
+          setbits = SPI_CR1_FPCLCKd2; /* 000: fPCLK/2 */
+          actual = priv->spiclock >> 1;
+        }
+      else if (frequency >= priv->spiclock >> 2)
+        {
+          /* Between fPCLCK/2 and fPCLCK/4, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd4; /* 001: fPCLK/4 */
+          actual = priv->spiclock >> 2;
+        }
+      else if (frequency >= priv->spiclock >> 3)
+        {
+          /* Between fPCLCK/4 and fPCLCK/8, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd8; /* 010: fPCLK/8 */
+          actual = priv->spiclock >> 3;
+        }
+      else if (frequency >= priv->spiclock >> 4)
+        {
+          /* Between fPCLCK/8 and fPCLCK/16, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd16; /* 011: fPCLK/16 */
+          actual = priv->spiclock >> 4;
+        }
+      else if (frequency >= priv->spiclock >> 5)
+        {
+          /* Between fPCLCK/16 and fPCLCK/32, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd32; /* 100: fPCLK/32 */
+          actual = priv->spiclock >> 5;
+        }
+      else if (frequency >= priv->spiclock >> 6)
+        {
+          /* Between fPCLCK/32 and fPCLCK/64, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd64; /*  101: fPCLK/64 */
+          actual = priv->spiclock >> 6;
+        }
+      else if (frequency >= priv->spiclock >> 7)
+        {
+          /* Between fPCLCK/64 and fPCLCK/128, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd128; /* 110: fPCLK/128 */
+          actual = priv->spiclock >> 7;
+        }
+      else
+        {
+          /* Less than fPCLK/128.  This is as slow as we can go */
+
+          setbits = SPI_CR1_FPCLCKd256; /* 111: fPCLK/256 */
+          actual = priv->spiclock >> 8;
+        }
+
+      spi_modifycr1(priv, 0, SPI_CR1_SPE);
+      spi_modifycr1(priv, setbits, SPI_CR1_BR_MASK);
+      spi_modifycr1(priv, SPI_CR1_SPE, 0);
+
+      /* Save the frequency selection so that subsequent reconfigurations
+       * will be faster.
+       */
+
+      spiinfo("Frequency %" PRIu32 "->%" PRIu32 "\n", frequency, actual);
+
+      priv->frequency = frequency;
+      priv->actual    = actual;
+    }
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.  see enum spi_mode_e for mode definitions
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The SPI mode requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      /* Yes... Set CR1 appropriately */
+
+      switch (mode)
+        {
+        case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+          setbits = 0;
+          clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+          setbits = SPI_CR1_CPHA;
+          clrbits = SPI_CR1_CPOL;
+          break;
+
+        case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+          setbits = SPI_CR1_CPOL;
+          clrbits = SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+          setbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          clrbits = 0;
+          break;
+
+#ifdef SPI_CR2_FRF    /* If MCU supports TI Synchronous Serial Frame Format */
+        case SPIDEV_MODETI:
+          setbits = 0;
+          clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          break;
+#endif
+
+        default:
+          return;
+        }
+
+        spi_modifycr1(priv, 0, SPI_CR1_SPE);
+        spi_modifycr1(priv, setbits, clrbits);
+        spi_modifycr1(priv, SPI_CR1_SPE, 0);
+
+#ifdef SPI_CR2_FRF    /* If MCU supports TI Synchronous Serial Frame Format */
+      switch (mode)
+        {
+          case SPIDEV_MODE0:
+          case SPIDEV_MODE1:
+          case SPIDEV_MODE2:
+          case SPIDEV_MODE3:
+            setbits = 0;
+            clrbits = SPI_CR2_FRF;
+            break;
+
+          case SPIDEV_MODETI:
+            setbits = SPI_CR2_FRF;
+            clrbits = 0;
+            break;
+
+          default:
+            return;
+        }
+
+      spi_modifycr1(priv, 0, SPI_CR1_SPE);
+      spi_modifycr2(priv, setbits, clrbits);
+      spi_modifycr1(priv, SPI_CR1_SPE, 0);
+#endif
+
+        /* Save the mode so that subsequent re-configurations will be
+         * faster
+         */
+
+        priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: spi_setbits
+ *
+ * Description:
+ *   Set the number of bits per word.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *   nbits - The number of bits requested
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  /* Has the number of bits changed? */
+
+  if (nbits != priv->nbits)
+    {
+      if (nbits < 4 || nbits > 16)
+        {
+          spierr("ERROR: nbits out of range: %d. Supported range [4..16].\n",
+                 nbits);
+          return;
+        }
+
+      spiinfo("nbits=%d (previous val %d)\n", nbits, priv->nbits);
+
+      clrbits = SPI_CR2_DS_MASK;
+      setbits = SPI_CR2_DS(nbits);
+
+      /* If nbits is <=8, then we are in byte mode and FRXTH must be set
+       * (else, transaction will not complete).
+       */
+
+      if (nbits < 9)
+        {
+          setbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 1 byte */
+        }
+      else
+        {
+          clrbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 2 bytes */
+        }
+
+      spi_modifycr1(priv, 0, SPI_CR1_SPE);
+      spi_modifycr2(priv, setbits, clrbits);
+      spi_modifycr1(priv, SPI_CR1_SPE, 0);
+      /* Save the selection so that subsequent re-configurations will be
+       * faster.
+       */
+
+      priv->nbits = nbits;
+    }
+  else
+    {
+      spiinfo("nbits=%d (CR2 has proper value, no need to update)\n", nbits);
+    }
+}
+
+/****************************************************************************
+ * Name: 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 spi_hwfeatures(struct spi_dev_s *dev,
+                          spi_hwfeatures_t features)
+{
+#if defined(CONFIG_SPI_BITORDER) || defined(CONFIG_SPI_TRIGGER)
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+#endif
+
+#ifdef CONFIG_SPI_BITORDER
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("features=%08x\n", features);
+
+  /* Transfer data LSB first? */
+
+  if ((features & HWFEAT_LSBFIRST) != 0)
+    {
+      setbits = SPI_CR1_LSBFIRST;
+      clrbits = 0;
+    }
+  else
+    {
+      setbits = 0;
+      clrbits = SPI_CR1_LSBFIRST;
+    }
+
+  spi_modifycr1(priv, 0, SPI_CR1_SPE);
+  spi_modifycr1(priv, setbits, clrbits);
+  spi_modifycr1(priv, SPI_CR1_SPE, 0);
+
+  features &= ~HWFEAT_LSBFIRST;
+#endif
+
+#ifdef CONFIG_SPI_TRIGGER
+/* Turn deferred trigger mode on or off.  Only applicable for DMA mode. If a
+ * transfer is deferred then the DMA will not actually be triggered until a
+ * subsequent call to SPI_TRIGGER to set it off. The thread will be waiting
+ * on the transfer completing as normal.
+ */
+
+  priv->defertrig = ((features & HWFEAT_TRIGGER) != 0);
+  features &= ~HWFEAT_TRIGGER;
+#endif
+
+  /* Other H/W features are not supported */
+
+  return (features == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_send
+ *
+ * Description:
+ *   Exchange 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:
+ *   response
+ *
+ ****************************************************************************/
+
+static uint32_t spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  uint32_t regval;
+  uint32_t ret;
+
+  DEBUGASSERT(priv && priv->spibase);
+
+  spi_writeword(priv, (uint32_t)(wd & 0xffff));
+  ret = (uint32_t)spi_readword(priv);
+
+  /* Check and clear any error flags
+   * (Reading from the SR clears the error flags)
+   */
+
+  regval = spi_getreg(priv, STM32WL5_SPI_SR_OFFSET);
+
+  spiinfo("Sent: %04" PRIx32 " Return: %04" PRIx32
+          " Status: %02" PRIx32 "\n", wd, ret, regval);
+  UNUSED(regval);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_exchange (no DMA).  aka spi_exchange_nodma
+ *
+ * Description:
+ *   Exchange a block of data on SPI without using DMA
+ *
+ *   REVISIT:
+ *   This function could be much more efficient by exploiting (1) RX and TX
+ *   FIFOs and (2) the STM32 F3 data packing.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to a buffer in which to receive data
+ *   nwords   - the length of data 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
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_STM32WL5_SPI_DMA) || defined(CONFIG_STM32WL5_DMACAPABLE) || \
+     defined(CONFIG_STM32WL5_SPI_DMATHRESHOLD)
+#if !defined(CONFIG_STM32WL5_SPI_DMA)
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+#else
+static void spi_exchange_nodma(struct spi_dev_s *dev,
+                               const void *txbuffer,
+                               void *rxbuffer, size_t nwords)
+#endif
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  DEBUGASSERT(priv && priv->spibase);
+
+  spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
+
+  /* 8- or 16-bit mode? */
+
+  if (priv->nbits > 8)
+    {
+      /* 16-bit mode */
+
+      const uint16_t *src  = (const uint16_t *)txbuffer;
+      uint16_t *dest = (uint16_t *)rxbuffer;
+      uint16_t  word;
+
+      while (nwords-- > 0)
+        {
+          /* Get the next word to write.  Is there a source buffer? */
+
+          if (src)
+            {
+              word = *src++;
+            }
+          else
+            {
+              word = 0xffff;
+            }
+
+          /* Exchange one word */
+
+          word = (uint16_t)spi_send(dev, (uint32_t)word);
+
+          /* Is there a buffer to receive the return value? */
+
+          if (dest)
+            {
+              *dest++ = word;
+            }
+        }
+    }
+  else
+    {
+      /* 8-bit mode */
+
+      const uint8_t *src  = (const uint8_t *)txbuffer;
+            uint8_t *dest = (uint8_t *)rxbuffer;
+            uint8_t  word;
+
+      while (nwords-- > 0)
+        {
+          /* Get the next word to write.  Is there a source buffer? */
+
+          if (src)
+            {
+              word = *src++;
+            }
+          else
+            {
+              word = 0xff;
+            }
+
+          /* Exchange one word */
+
+          word = (uint8_t)spi_send(dev, (uint32_t)word);
+
+          /* Is there a buffer to receive the return value? */
+
+          if (dest)
+            {
+              *dest++ = word;
+            }
+        }
+    }
+}
+#endif /* !CONFIG_STM32WL5_SPI_DMA || CONFIG_STM32WL5_DMACAPABLE || CONFIG_STM32WL5_SPI_DMATHRESHOLD */
+
+/****************************************************************************
+ * Name: spi_exchange (with DMA capability)
+ *
+ * Description:
+ *   Exchange a block of data on SPI using DMA
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to a buffer in which to receive data
+ *   nwords   - the length of data to be exchanged in units of words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  void *xbuffer = rxbuffer;
+  int ret;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Convert the number of word to a number of bytes */
+
+  size_t nbytes = (priv->nbits > 8) ? nwords << 1 : nwords;
+
+#ifdef CONFIG_STM32WL5_SPI_DMATHRESHOLD
+  /* If this is a small SPI transfer, then let spi_exchange_nodma() do the
+   * work.
+   */
+
+  if (nbytes <= CONFIG_STM32WL5_SPI_DMATHRESHOLD)
+    {
+      spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
+      return;
+    }
+#endif
+
+  if ((priv->rxdma == NULL) || (priv->txdma == NULL) ||
+      up_interrupt_context())
+    {
+      /* Invalid DMA channels, or interrupt context, fall
+       * back to non-DMA method.
+       */
+
+      spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
+      return;
+    }
+
+#ifdef CONFIG_STM32WL5_DMACAPABLE
+  if ((txbuffer && priv->txbuf == 0 &&
+      !stm32wl5_dmacapable((uintptr_t)txbuffer, nwords, priv->txccr)) ||
+      (rxbuffer && priv->rxbuf == 0 &&
+       !stm32wl5_dmacapable((uintptr_t)rxbuffer, nwords, priv->rxccr)))
+    {
+      /* Unsupported memory region fall back to non-DMA method. */
+
+      spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
+    }
+  else
+#endif
+    {
+      static uint16_t rxdummy = 0xffff;
+      static const uint16_t txdummy = 0xffff;
+
+      spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n",
+               txbuffer, rxbuffer, nwords);
+      DEBUGASSERT(priv && priv->spibase);
+
+      /* Setup DMAs */
+
+      /* If this bus uses a in driver buffers we will incur 2 copies,
+       * The copy cost is << less the non DMA transfer time and having
+       * the buffer in the driver ensures DMA can be used. This is because
+       * the API does not support passing the buffer extent so the only
+       * extent is buffer + the transfer size. These can sizes be less than
+       * the cache line size, and not aligned and typically greater then 4
+       * bytes, which is about the break even point for the DMA IO overhead.
+       */
+
+      if (txbuffer && priv->txbuf)
+        {
+          if (nbytes > priv->buflen)
+            {
+              nbytes = priv->buflen;
+            }
+
+          memcpy(priv->txbuf, txbuffer, nbytes);
+          txbuffer  = priv->txbuf;
+          rxbuffer  = rxbuffer ? priv->rxbuf : rxbuffer;

Review Comment:
   It was code from stm32. I suppose that it was wrong and the code for stm32 should be verified.



-- 
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