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

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

commit 48c88f2af36c235262eb74a3e1810dd1ab75cebe
Author: Jukka Laitinen <jukka.laiti...@intel.com>
AuthorDate: Thu Sep 12 12:29:11 2019 +0300

    arch/arm/src/stm32h7: Add the spi slave bus control driver
    
    This is a work in progress, and now only serves as
    DMA enabled simplex RX-only mode bus controller
    
    Signed-off-by: Jukka Laitinen <jukka.laiti...@intel.com>
---
 arch/arm/src/stm32h7/Make.defs         |    4 +
 arch/arm/src/stm32h7/stm32_spi.h       |   16 +
 arch/arm/src/stm32h7/stm32_spi_slave.c | 1806 ++++++++++++++++++++++++++++++++
 3 files changed, 1826 insertions(+)

diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs
index 44fe8dc..8cbc15b 100644
--- a/arch/arm/src/stm32h7/Make.defs
+++ b/arch/arm/src/stm32h7/Make.defs
@@ -180,6 +180,10 @@ ifeq ($(CONFIG_STM32H7_SPI),y)
 CHIP_CSRCS += stm32_spi.c
 endif
 
+ifeq ($(CONFIG_SPI_SLAVE),y)
+CHIP_CSRCS += stm32_spi_slave.c
+endif
+
 ifeq ($(CONFIG_STM32H7_SDMMC),y)
 CHIP_CSRCS += stm32_sdmmc.c
 endif
diff --git a/arch/arm/src/stm32h7/stm32_spi.h b/arch/arm/src/stm32h7/stm32_spi.h
index 3e4228c..d59a93b 100644
--- a/arch/arm/src/stm32h7/stm32_spi.h
+++ b/arch/arm/src/stm32h7/stm32_spi.h
@@ -83,6 +83,22 @@ struct spi_dev_s; /* Forward reference */
 FAR struct spi_dev_s *stm32_spibus_initialize(int bus);
 
 /*****************************************************************************
+ * Name: stm32_spi_slave_initialize
+ *
+ * Description:
+ *   Initialize the selected SPI bus for slave operation
+ *
+ * Input Parameters:
+ *   bus number
+ *
+ * Returned Value:
+ *   Valid SPI device structure reference on succcess; a NULL on failure
+ *
+ *****************************************************************************/
+
+FAR struct spi_sctrlr_s *stm32_spi_slave_initialize(int bus);
+
+/*****************************************************************************
  * Name:  stm32_spi1/2/...select and stm32_spi1/2/...status
  *
  * Description:
diff --git a/arch/arm/src/stm32h7/stm32_spi_slave.c 
b/arch/arm/src/stm32h7/stm32_spi_slave.c
new file mode 100644
index 0000000..e2e6025
--- /dev/null
+++ b/arch/arm/src/stm32h7/stm32_spi_slave.c
@@ -0,0 +1,1806 @@
+/*****************************************************************************
+ * arch/arm/src/stm32h7/stm32_spi_slave.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>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <debug.h>
+#include <string.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/spi/slave.h>
+#include <nuttx/power/pm.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "arm_arch.h"
+
+#include "chip.h"
+#include "stm32_rcc.h"
+#include "stm32_gpio.h"
+#include "stm32_spi.h"
+#include "stm32_dma.h"
+
+#if defined(CONFIG_STM32H7_SPI1_SLAVE) ||                               \
+  defined(CONFIG_STM32H7_SPI2_SLAVE) ||                                 \
+  defined(CONFIG_STM32H7_SPI3_SLAVE) ||                                 \
+  defined(CONFIG_STM32H7_SPI4_SLAVE) ||                                 \
+  defined(CONFIG_STM32H7_SPI5_SLAVE) ||                                 \
+  defined(CONFIG_STM32H7_SPI6_SLAVE)
+
+/*****************************************************************************
+ * Pre-processor Definitions
+ *****************************************************************************/
+
+/* Configuration *************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32H7_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32H7_SPI_INTERRUPTS) && defined(CONFIG_STM32H7_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  elif defined(DMA_SCR_PRIMED)
+#    define SPI_DMA_PRIO  DMA_SCR_PRILO
+#  else
+#    error "Unknown STM32 DMA"
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_SCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+/* DMA channel configuration */
+
+#  define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO | DMA_SCR_MSIZE_16BITS | \
+                                     DMA_SCR_PSIZE_16BITS | DMA_SCR_MINC | \
+                                     DMA_SCR_DIR_P2M | DMA_SCR_CIRC)
+#  define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO | DMA_SCR_MSIZE_8BITS | \
+                                     DMA_SCR_PSIZE_8BITS | DMA_SCR_MINC| \
+                                     DMA_SCR_DIR_P2M | DMA_SCR_CIRC)
+#  define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO | DMA_SCR_MSIZE_16BITS| \
+                                     DMA_SCR_PSIZE_16BITS | DMA_SCR_MINC| \
+                                     DMA_SCR_DIR_M2P)
+#  define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO | DMA_SCR_MSIZE_8BITS|  \
+                                     DMA_SCR_PSIZE_8BITS | 
DMA_SCR_MINC|DMA_SCR_DIR_M2P)
+#endif
+
+/* Kernel clock configuration
+ * TODO:
+ *  - support for all kernel clock configuration
+ */
+
+#if defined(CONFIG_STM32H7_SPI1_SLAVE) || defined(CONFIG_STM32H7_SPI2_SLAVE) 
|| \
+  defined(CONFIG_STM32H7_SPI3_SLAVE)
+#  if STM32_RCC_D2CCIP1R_SPI123SRC == RCC_D2CCIP1R_SPI123SEL_PLL1
+#    define SPI123_KERNEL_CLOCK_FREQ STM32_PLL1Q_FREQUENCY
+#  else
+#    error Not supported yet
+#  endif
+#  if SPI123_KERNEL_CLOCK_FREQ > 200000000
+#    error Not supported SPI123 frequency
+#  endif
+#endif  /* SPI123 */
+
+#if defined(CONFIG_STM32H7_SPI4_SLAVE) || defined(CONFIG_STM32H7_SPI5_SLAVE)
+#  if STM32_RCC_D2CCIP1R_SPI45SRC == RCC_D2CCIP1R_SPI45SEL_APB
+#    define SPI45_KERNEL_CLOCK_FREQ STM32_PCLK2_FREQUENCY
+#  else
+#    error Not supported yet
+#  endif
+#  if SPI45_KERNEL_CLOCK_FREQ > 100000000
+#    error Not supported SPI45 frequency
+#  endif
+#endif  /* SPI45 */
+
+#if defined(CONFIG_STM32H7_SPI6_SLAVE)
+#  if STM32_RCC_D3CCIPR_SPI6SRC == RCC_D3CCIPR_SPI6SEL_PCLK4
+#    define SPI6_KERNEL_CLOCK_FREQ STM32_PCLK4_FREQUENCY
+#  else
+#    error Not supported yet
+#  endif
+#  if SPI6_KERNEL_CLOCK_FREQ > 100000000
+#    error Not supported SPI6 frequency
+#  endif
+#endif  /* SPI6 */
+
+#if defined (CONFIG_STM32H7_SPI_SLAVE_QSIZE)
+#  if CONFIG_STM32H7_SPI_SLAVE_QSIZE > 65535
+#    error CONFIG_STM32H7_SPI_SLAVE_QSIZE too large
+#  endif
+#endif
+
+/* SPI6 is in D3 domain and is not yet supported. Remove this when the proper
+ * support is in place
+ */
+
+#if defined (CONFIG_STM32H7_SPI6_SLAVE)
+#    error SPI6 slave not supported yet
+#endif
+
+#define DMA_BUFFER_MASK    (ARMV7M_DCACHE_LINESIZE - 1)
+#define DMA_ALIGN_UP(n)    (((n) + DMA_BUFFER_MASK) & ~DMA_BUFFER_MASK)
+#define DMA_ALIGN_DOWN(n)  ((n) & ~DMA_BUFFER_MASK)
+
+/*****************************************************************************
+ * Private Types
+ *****************************************************************************/
+
+enum spi_config_e
+{
+  FULL_DUPLEX = 0,
+  SIMPLEX_TX,
+  SIMPLEX_RX,
+  HALF_DUPLEX
+};
+
+struct stm32_spidev_s
+{
+  struct spi_sctrlr_s sctrlr;    /* Externally visible part of the
+                                  * SPI slave interface */
+  struct spi_sdev_s *sdev;       /* Bound SPI slave device interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+  uint8_t          irq;          /* SPI IRQ number */
+  uint32_t         nss_pin;      /* Chip select pin configuration */
+#ifdef CONFIG_STM32H7_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the TX DMA */
+  uint32_t         rxch;         /* The RX DMA channel number */
+  uint32_t         txch;         /* The TX DMA channel number */
+  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 */
+  bool             dmarunning;   /* DMA is started */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual
+                                  * exclusion */
+  int8_t           nbits;        /* Width of word in bits */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+  uint8_t          bus;          /* SPI bus number */
+  bool             nss;          /* True: Chip selected */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+  enum spi_config_e config;      /* full/half duplex, simplex rx/tx */
+
+  /* Output queue */
+
+  uint16_t ohead;                 /* Location of next value in out queue */
+  uint16_t otail;                 /* Index of first value in out queue */
+  uint8_t *outq;
+
+  /* Input queue */
+
+  uint16_t ihead;                 /* Location of next unread value */
+#ifndef CONFIG_STM32H7_SPI_DMA
+  uint16_t itail;                 /* Index of next free memory pointer */
+#endif
+  uint8_t *inq;
+};
+
+/*****************************************************************************
+ * Private Function Prototypes
+ *****************************************************************************/
+
+/* Helpers */
+
+static inline uint32_t spi_getreg(FAR struct stm32_spidev_s *priv,
+                                  uint32_t offset);
+static inline void spi_putreg(FAR struct stm32_spidev_s *priv,
+                              uint32_t offset, uint32_t value);
+static inline void spi_modifyreg(FAR struct stm32_spidev_s *priv,
+                                 uint32_t offset, uint32_t clrbits,
+                                 uint32_t setbits);
+static inline uint32_t spi_readword(FAR struct stm32_spidev_s *priv);
+static inline void spi_writeword(FAR struct stm32_spidev_s *priv,
+                                 uint32_t byte);
+static inline bool spi_9to16bitmode(FAR struct stm32_spidev_s *priv);
+#ifdef CONFIG_DEBUG_SPI_INFO
+static inline void spi_dumpregs(FAR struct stm32_spidev_s *priv);
+#endif
+
+/* DMA support */
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+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(FAR struct stm32_spidev_s *priv,
+                                  size_t nwords);
+static void        spi_dmatxsetup(FAR struct stm32_spidev_s *priv,
+                                  size_t nwords);
+static inline void spi_dmarxstart(FAR struct stm32_spidev_s *priv);
+static inline void spi_dmatxstart(FAR struct stm32_spidev_s *priv);
+#endif
+
+/* Interrupt handler detecting nss status changes */
+
+static int  spi_nssinterrupt(int irq, void *context, void *arg);
+
+/* SPI slave methods */
+
+static void     spi_bind(struct spi_sctrlr_s *sctrlr,
+                  struct spi_sdev_s *sdev, enum spi_smode_e mode,
+                  int nbits);
+static void     spi_unbind(struct spi_sctrlr_s *sctrlr);
+static int      spi_enqueue(struct spi_sctrlr_s *sctrlr,
+                            FAR const void *data,
+                            size_t len);
+static bool     spi_qfull(struct spi_sctrlr_s *sctrlr);
+static void     spi_qflush(struct spi_sctrlr_s *sctrlr);
+static size_t   spi_qpoll(struct spi_sctrlr_s *sctrlr);
+
+/* Initialization */
+
+static void     spi_slave_initialize(FAR struct stm32_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(FAR struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/*****************************************************************************
+ * Private Data
+ *****************************************************************************/
+
+/* SPI slave controller driver operations */
+
+static const struct spi_sctrlrops_s g_sctrlr_ops =
+{
+  .bind              = spi_bind,
+  .unbind            = spi_unbind,
+  .enqueue           = spi_enqueue,
+  .qfull             = spi_qfull,
+  .qflush            = spi_qflush,
+  .qpoll             = spi_qpoll,
+};
+
+#define SPI_SLAVE_OUTQ(x) spi##x##_outq
+#define SPI_SLAVE_INQ(x) spi##x##_inq
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+#define SPI_SLAVE_INIT_DMA(x)                           \
+  .rxch          = DMAMAP_SPI##x##_RX,                  \
+  .txch          = DMAMAP_SPI##x##_TX,                  \
+  .outq                 = SPI_SLAVE_OUTQ(x),                   \
+  .inq          = SPI_SLAVE_INQ(x),
+#else
+#define SPI_SLAVE_INIT_DMA(x)
+#endif
+
+#ifdef CONFIG_PM
+#define SPI_SLAVE_INIT_PM_PREPARE .pm_cb.prepare = spi_pm_prepare,
+#else
+#define SPI_SLAVE_INIT_PM_PREPARE
+#endif
+
+#define SPI_SLAVE_INIT(x)                               \
+{                                                       \
+  .sctrlr.ops    = &g_sctrlr_ops,                       \
+  .sdev          = NULL,                                \
+  .spibase       = STM32_SPI##x##_BASE,                 \
+  .spiclock      = SPI45_KERNEL_CLOCK_FREQ,             \
+  .irq           = STM32_IRQ_SPI##x,                    \
+  SPI_SLAVE_INIT_DMA(x)                                 \
+  .initialized   = false,                               \
+  SPI_SLAVE_INIT_PM_PREPARE                             \
+  .config        = CONFIG_STM32H7_SPI##x##_COMMTYPE,    \
+}
+
+#ifdef CONFIG_STM32H7_SPI1_SLAVE
+
+static uint8_t SPI_SLAVE_OUTQ(1)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static uint8_t SPI_SLAVE_INQ(1)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static struct stm32_spidev_s g_spi1sctrlr = SPI_SLAVE_INIT(1);
+
+#endif
+
+#ifdef CONFIG_STM32H7_SPI2_SLAVE
+
+static uint8_t SPI_SLAVE_OUTQ(2)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static uint8_t SPI_SLAVE_INQ(2)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static struct stm32_spidev_s g_spi2sctrlr = SPI_SLAVE_INIT(2);
+
+#endif
+
+#ifdef CONFIG_STM32H7_SPI3_SLAVE
+
+static uint8_t SPI_SLAVE_OUTQ(3)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static uint8_t SPI_SLAVE_INQ(3)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static struct stm32_spidev_s g_spi3sctrlr = SPI_SLAVE_INIT(3);
+
+#endif
+
+#ifdef CONFIG_STM32H7_SPI4_SLAVE
+
+static uint8_t SPI_SLAVE_OUTQ(4)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static uint8_t SPI_SLAVE_INQ(4)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static struct stm32_spidev_s g_spi4sctrlr = SPI_SLAVE_INIT(4);
+
+#endif
+
+#ifdef CONFIG_STM32H7_SPI5_SLAVE
+
+static uint8_t SPI_SLAVE_OUTQ(5)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static uint8_t SPI_SLAVE_INQ(5)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static struct stm32_spidev_s g_spi5sctrlr = SPI_SLAVE_INIT(5);
+
+#endif
+
+#ifdef CONFIG_STM32H7_SPI6_SLAVE
+
+/* TODO: these needs to be located in SRAM3 for SPI6 */
+
+static uint8_t SPI_SLAVE_OUTQ(6)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static uint8_t SPI_SLAVE_INQ(6)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)]
+__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+static struct stm32_spidev_s g_spi6sctrlr = SPI_SLAVE_INIT(6);
+
+#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 8-bit register
+ *
+ *****************************************************************************/
+
+static inline uint8_t spi_getreg8(FAR struct stm32_spidev_s *priv,
+                                  uint32_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/*****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 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 8-bit value to be written
+ *
+ *****************************************************************************/
+
+static inline void spi_putreg8(FAR struct stm32_spidev_s *priv,
+                               uint32_t offset, uint8_t value)
+{
+  putreg8(value, 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 uint32_t spi_getreg(FAR struct stm32_spidev_s *priv,
+                                  uint32_t offset)
+{
+  return getreg32(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
+ *
+ *****************************************************************************/
+
+static inline void spi_putreg(FAR struct stm32_spidev_s *priv,
+                              uint32_t offset, uint32_t value)
+{
+  putreg32(value, priv->spibase + offset);
+}
+
+/*****************************************************************************
+ * Name: spi_modifyreg
+ *
+ * Description:
+ *   Write a 32-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv    - private SPI device structure
+ *   offset  - offset to the register of interest
+ *   clrbits - the bits to clear
+ *   setbits - the bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ *****************************************************************************/
+
+static inline void spi_modifyreg(FAR struct stm32_spidev_s *priv,
+                                 uint32_t offset, uint32_t clrbits,
+                                 uint32_t setbits)
+{
+  modifyreg32(priv->spibase + offset, clrbits, setbits);
+}
+
+/*****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ *****************************************************************************/
+
+static inline uint32_t spi_readword(FAR struct stm32_spidev_s *priv)
+{
+  /* Can't receive in tx only mode */
+
+  if (priv->config == SIMPLEX_TX)
+    {
+      return 0;
+    }
+
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_RXP) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32_SPI_RXDR_OFFSET);
+}
+
+/*****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one byte to SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ *****************************************************************************/
+
+static inline void spi_writeword(FAR struct stm32_spidev_s *priv,
+                                 uint32_t word)
+{
+  /* Can't transmit in rx only mode */
+
+  if (priv->config == SIMPLEX_RX)
+    {
+      return;
+    }
+
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_TXP) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32_SPI_TXDR_OFFSET, word);
+}
+
+/*****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ *****************************************************************************/
+
+static inline uint8_t spi_readbyte(FAR struct stm32_spidev_s *priv)
+{
+  /* Can't receive in tx only mode */
+
+  if (priv->config == SIMPLEX_TX)
+    {
+      return 0;
+    }
+
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_RXP) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32_SPI_RXDR_OFFSET);
+}
+
+/*****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ *****************************************************************************/
+
+static inline void spi_writebyte(FAR struct stm32_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Can't transmit in rx only mode */
+
+  if (priv->config == SIMPLEX_RX)
+    {
+      return;
+    }
+
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_TXP) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32_SPI_TXDR_OFFSET, byte);
+}
+
+/*****************************************************************************
+ * Name: spi_dumpregs
+ *****************************************************************************/
+
+#ifdef CONFIG_DEBUG_SPI_INFO
+static void spi_dumpregs(FAR struct stm32_spidev_s *priv)
+{
+  spiinfo("CR1: 0x%08x CFG1: 0x%08x CFG2: 0x%08x\n",
+          spi_getreg(priv, STM32_SPI_CR1_OFFSET),
+          spi_getreg(priv, STM32_SPI_CFG1_OFFSET),
+          spi_getreg(priv, STM32_SPI_CFG2_OFFSET));
+  spiinfo("IER: 0x%08x SR: 0x%08x I2SCFGR: 0x%08x\n",
+          spi_getreg(priv, STM32_SPI_IER_OFFSET),
+          spi_getreg(priv, STM32_SPI_SR_OFFSET),
+          spi_getreg(priv, STM32_SPI_I2SCFGR_OFFSET));
+}
+#endif
+
+/*****************************************************************************
+ * Name: spi_9to16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in more then 8 bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: >8 bit mode-bit mode, false: <= 8-bit mode
+ *
+ *****************************************************************************/
+
+static inline bool spi_9to16bitmode(FAR struct stm32_spidev_s *priv)
+{
+  uint32_t regval = spi_getreg(priv, STM32_SPI_CFG1_OFFSET);
+
+  return ((regval & SPI_CFG1_CRCSIZE_9BIT) == SPI_CFG1_CRCSIZE_9BIT);
+}
+
+/*****************************************************************************
+ * Name: spi_dmarxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ *****************************************************************************/
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->rxresult = isr | 0x080;  /* OR'ed with 0x80 to assure non-zero */
+}
+#endif
+
+/*****************************************************************************
+ * Name: spi_dmatxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ *****************************************************************************/
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->txresult = isr | 0x080;  /* OR'ed with 0x80 to assure non-zero */
+}
+#endif
+
+/*****************************************************************************
+ * Name: spi_dmarxsetup
+ *
+ * Description:
+ *   Setup to perform RX DMA
+ *
+ *****************************************************************************/
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+static void spi_dmarxsetup(FAR struct stm32_spidev_s *priv, size_t nwords)
+{
+  stm32_dmacfg_t dmacfg;
+
+  /* Can't receive in tx only mode */
+
+  if (priv->config == SIMPLEX_TX)
+    {
+      return;
+    }
+
+  /* 8- or 16-bit mode? */
+
+  if (spi_9to16bitmode(priv))
+    {
+      /* 16-bit mode */
+
+      priv->rxccr = SPI_RXDMA16_CONFIG;
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to receive data in? */
+
+      priv->rxccr = SPI_RXDMA8_CONFIG;
+    }
+
+  /* Configure the RX DMA */
+
+  /* This just transfers continuously to circular buffer */
+
+  dmacfg.paddr = priv->spibase + STM32_SPI_RXDR_OFFSET;
+  dmacfg.maddr = (uint32_t)priv->inq;
+  dmacfg.ndata = CONFIG_STM32H7_SPI_SLAVE_QSIZE;
+  dmacfg.cfg1  = priv->rxccr;
+  dmacfg.cfg2  = 0;
+
+  stm32_dmasetup(priv->rxdma, &dmacfg);
+}
+#endif
+
+/*****************************************************************************
+ * Name: spi_dmatxsetup
+ *
+ * Description:
+ *   Setup to perform TX DMA
+ *
+ *****************************************************************************/
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+static void spi_dmatxsetup(FAR struct stm32_spidev_s *priv, size_t nwords)
+{
+  /* TODO: set up dma to transfer out the new data from priv->outq,
+   * which is set up in spi_enqueue
+   * When the transfer is complete, update outq indexies accordingly
+   */
+
+#if 0
+  stm32_dmacfg_t dmacfg;
+
+  /* Can't transmit in rx only mode */
+
+  if (priv->config == SIMPLEX_RX)
+    {
+      return;
+    }
+
+  /* 8- or 16-bit mode? */
+
+  if (spi_9to16bitmode(priv))
+    {
+      /* 16-bit mode */
+
+      priv->txccr = SPI_TXDMA16_CONFIG;
+    }
+  else
+    {
+      /* 8-bit mode */
+
+      priv->txccr = SPI_TXDMA8_CONFIG;
+    }
+
+  dmacfg.paddr = priv->spibase + STM32_SPI_TXDR_OFFSET;
+  dmacfg.maddr = (uint32_t)priv->outq[0];
+  dmacfg.ndata = nwords;
+  dmacfg.cfg1  = priv->txccr;
+  dmacfg.cfg2  = 0;
+
+  /* Setup the TX DMA */
+
+  stm32_dmasetup(priv->txdma, &dmacfg);
+
+#endif // 0
+}
+#endif
+
+/*****************************************************************************
+ * Name: spi_dmarxstart
+ *
+ * Description:
+ *   Start RX DMA. NB! This must be called before spi_dmatxstart
+ *
+ *****************************************************************************/
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+static void spi_dmarxstart(FAR struct stm32_spidev_s *priv)
+{
+  /* Can't receive in tx only mode */
+
+  if (priv->config == SIMPLEX_TX)
+    {
+      return;
+    }
+
+  priv->rxresult = 0;
+
+  spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_RXDMAEN);
+  stm32_dmastart(priv->rxdma, spi_dmarxcallback, priv, false);
+}
+#endif
+
+/*****************************************************************************
+ * Name: spi_dmatxstart
+ *
+ * Description:
+ *   Start TX DMA. NB! This must be called after spi_dmarxstart
+ *
+ *****************************************************************************/
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+static void spi_dmatxstart(FAR struct stm32_spidev_s *priv)
+{
+  /* Can't transmit in rx only mode */
+
+  if (priv->config == SIMPLEX_RX)
+    {
+      return;
+    }
+
+  priv->txresult = 0;
+  stm32_dmastart(priv->txdma, spi_dmatxcallback, priv, false);
+  spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_TXDMAEN);
+}
+#endif
+
+/*****************************************************************************
+ * Name: spi_lock
+ *
+ * Description:
+ *   Take or release the semaphore that enforces mutually exclusive access to
+ *   SPI resources, handling any exceptional conditions
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   lock - true: Lock spi bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   None
+ *
+ *****************************************************************************/
+
+static int spi_lock(FAR struct spi_sctrlr_s *sctrlr, bool lock)
+{
+  FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)sctrlr;
+  int ret;
+
+  if (lock)
+    {
+      /* Take the semaphore (perhaps waiting) */
+
+      do
+        {
+          ret = nxsem_wait(&priv->exclsem);
+
+          /* The only case that an error should occur here is if the wait
+           * was awakened by a signal.
+           */
+
+          DEBUGASSERT(ret == OK || ret == -EINTR);
+        }
+      while (ret == -EINTR);
+    }
+  else
+    {
+      (void)nxsem_post(&priv->exclsem);
+      ret = OK;
+    }
+
+  return ret;
+}
+
+/*****************************************************************************
+ * Name: spi_enable
+ *
+ * Description:
+ *   Enable/disable the SPI peripheral
+ *
+ * Input Parameters:
+ *   priv  - Device-specific state data
+ *   state - true: enable, false: disable
+ *
+ *****************************************************************************/
+
+static inline void spi_enable(FAR struct stm32_spidev_s *priv, bool state)
+{
+  if (state == true)
+    {
+      /* Enable SPI */
+
+      spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, 0, SPI_CR1_SPE);
+    }
+  else
+    {
+      /* Disable SPI */
+
+      spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, SPI_CR1_SPE, 0);
+    }
+}
+
+/*****************************************************************************
+ * 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(FAR struct spi_sctrlr_s *sctrlr, enum spi_mode_e mode)
+{
+  FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)sctrlr;
+  uint32_t setbits = 0;
+  uint32_t clrbits = 0;
+
+  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_CFG2_CPOL | SPI_CFG2_CPHA;
+          break;
+
+        case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+          setbits = SPI_CFG2_CPHA;
+          clrbits = SPI_CFG2_CPOL;
+          break;
+
+        case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+          setbits = SPI_CFG2_CPOL;
+          clrbits = SPI_CFG2_CPHA;
+          break;
+
+        case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+          setbits = SPI_CFG2_CPOL | SPI_CFG2_CPHA;
+          clrbits = 0;
+          break;
+
+        default:
+          return;
+        }
+
+      /* Change SPI mode */
+
+      spi_modifyreg(priv, STM32_SPI_CFG2_OFFSET, clrbits, setbits);
+
+      /* 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(FAR struct spi_sctrlr_s *sctrlr, int nbits)
+{
+  FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)sctrlr;
+  uint32_t setbits = 0;
+  uint32_t clrbits = 0;
+  int savbits = nbits;
+
+  spiinfo("nbits=%d\n", nbits);
+
+  /* Has the number of bits changed? */
+
+  if (nbits != priv->nbits)
+    {
+      /* Yes... Set CFG1 appropriately */
+
+      /* Set the number of bits (valid range 4-32) */
+
+      if (nbits < 4 || nbits > 32)
+        {
+          return;
+        }
+
+      clrbits = SPI_CFG1_DSIZE_MASK;
+      setbits = SPI_CFG1_DSIZE_VAL(nbits);
+
+      /* REVISIT: FIFO threshold level */
+
+      /* If nbits is <=8, then we are in byte mode and FRXTH shall be set
+       * (else, transaction will not complete).
+       */
+
+      if (nbits < 9)
+        {
+          setbits |= SPI_CFG1_FTHLV_1DATA; /* RX FIFO Threshold = 1 byte */
+        }
+      else
+        {
+          setbits |= SPI_CFG1_FTHLV_2DATA; /* RX FIFO Threshold = 2 byte */
+        }
+
+      spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, clrbits, setbits);
+
+      priv->nbits = savbits;
+    }
+}
+
+/*****************************************************************************
+ * Name: spi_bind
+ *
+ * Description:
+ *   Bind the SPI slave device interface to the SPI slave controller
+ *   interface and configure the SPI interface.  Upon return, the SPI
+ *   slave controller driver is fully operational and ready to perform
+ *   transfers.
+ *
+ * Input Parameters:
+ *   sctrlr - SPI slave controller interface instance
+ *   sdev   - SPI slave device interface instance
+ *   mode   - The SPI mode requested
+ *   nbits  - The number of bits requests.
+ *            If value is greater > 0 then it implies MSB first
+ *            If value is below < 0, then it implies LSB first with -nbits
+ *
+ * Returned Value:
+ *   none
+ *
+ *****************************************************************************/
+
+static void spi_bind(struct spi_sctrlr_s *sctrlr,
+                     struct spi_sdev_s *sdev, enum spi_smode_e mode,
+                     int nbits)
+{
+  struct stm32_spidev_s *priv = (struct stm32_spidev_s *)sctrlr;
+  uint32_t nss_gpio;
+
+  spiinfo("sdev=%p mode=%d nbits=%d\n", sdv, mode, nbits);
+
+  DEBUGASSERT(priv != NULL && priv->sdev == NULL && sdev != NULL);
+
+  /* Get exclusive access to the SPI device */
+
+  spi_lock(sctrlr, true);
+
+  /* Make sure the spi is disabled */
+
+  spi_enable(priv, false);
+
+  /* Make sure the dma is disabled */
+
+  spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET,
+                SPI_CFG1_RXDMAEN | SPI_CFG1_TXDMAEN, 0);
+
+  /* invalidate the whole rx buffer */
+
+  up_flush_dcache((uintptr_t)priv->inq,
+                  (uintptr_t)priv->inq + CONFIG_STM32H7_SPI_SLAVE_QSIZE);
+
+  /* Bind the SPI slave device interface instance to the SPI slave
+   * controller interface.
+   */
+
+  priv->sdev = sdev;
+
+  /* Initialize the circular buffer head */
+
+  priv->ihead = 0;
+
+  /* Setup to begin normal SPI operation */
+
+  spi_setmode(sctrlr, mode);
+  spi_setbits(sctrlr, nbits);
+
+  /* First, configure NSS as GPIO EXTI input */
+
+  nss_gpio = priv->nss_pin & (GPIO_PORT_MASK | GPIO_PIN_MASK);
+  nss_gpio |= (GPIO_INPUT | GPIO_FLOAT | GPIO_EXTI);
+  stm32_configgpio(nss_gpio);
+
+  /* Bind to NSS interrupt */
+
+  (void)stm32_gpiosetevent(priv->nss_pin, false, true, false,
+                           spi_nssinterrupt, priv);
+
+#ifdef CONFIG_PM
+  /* Register to receive power management callbacks */
+
+  ret = pm_register(&priv->pm_cb);
+  DEBUGASSERT(ret == OK);
+  UNUSED(ret);
+#endif
+
+  spi_lock(sctrlr, false);
+}
+
+/*****************************************************************************
+ * Name: spi_nssinterrupt
+ *
+ * Description:
+ *   SPI slave NSS interrupt handler
+ *
+ * Input Parameters:
+ *   irq     - not used
+ *   context - not used
+ *   arg     - pointer to controller
+ *
+ * Returned Value:
+ *   OK
+ *
+ *****************************************************************************/
+
+static int spi_nssinterrupt(int irq, void *context, void *arg)
+{
+  FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg;
+
+  /* If the pin is low, just enable rising edge interrupt */
+
+  if (!stm32_gpioread(priv->nss_pin))
+    {
+      /* Bind to NSS rising edge interrupt */
+
+      (void)stm32_gpiosetevent(priv->nss_pin, true, false, false,
+                               spi_nssinterrupt, priv);
+      return OK;
+    }
+
+  /* Disable NSS interrupt */
+
+  (void)stm32_gpiosetevent(priv->nss_pin, false, false, false,
+                           NULL, priv);
+
+  /* Re-configure nss pin */
+
+  stm32_configgpio(priv->nss_pin);
+
+  /* Enable spi peripheral */
+
+  spi_enable(priv, true);
+
+  /* Flush SPI read FIFO */
+
+  while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_RXPLVL_MASK) != 0)
+    {
+      spi_getreg(priv, STM32_SPI_RXDR_OFFSET);
+    }
+
+  /* Disable spi before dma setup */
+
+  spi_enable(priv, false);
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+
+  /* Setup DMAs */
+
+  spi_dmarxsetup(priv, 0);
+  spi_dmatxsetup(priv, 0);
+
+  /* Start the DMAs */
+
+  spi_dmarxstart(priv);
+  spi_dmatxstart(priv);
+
+  priv->dmarunning = true;
+#endif
+
+  /* Enable spi peripheral */
+
+  spi_enable(priv, true);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: spi_unbind
+ *
+ * Description:
+ *   Un-bind the SPI slave device interface from the SPI slave controller
+ *   interface.  Reset the SPI interface and restore the SPI slave
+ *   controller driver to its initial state,
+ *
+ * Input Parameters:
+ *   sctrlr - SPI slave controller interface instance
+ *
+ * Returned Value:
+ *   none
+ *
+ *****************************************************************************/
+
+static void spi_unbind(struct spi_sctrlr_s *sctrlr)
+{
+  struct stm32_spidev_s *priv = (struct stm32_spidev_s *)sctrlr;
+
+  DEBUGASSERT(priv != NULL);
+  spiinfo("Unbinding %p\n", priv->sdev);
+
+  DEBUGASSERT(priv->sdev != NULL);
+
+  /* Get exclusive access to the SPI device */
+
+  spi_lock(sctrlr, true);
+
+  /* Unbind the SPI slave interface */
+
+  priv->sdev = NULL;
+
+  /* Disable DMA */
+
+  spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET,
+                SPI_CFG1_RXDMAEN | SPI_CFG1_TXDMAEN, 0);
+
+  /* Disable the SPI peripheral */
+
+  spi_enable(priv, false);
+
+  spi_lock(sctrlr, false);
+}
+
+/*****************************************************************************
+ * Name: spi_enqueue
+ *
+ * Description:
+ *   Enqueue the next value to be shifted out from the interface.  This adds
+ *   the word the controller driver for a subsequent transfer but has no
+ *   effect on anyin-process or currently "committed" transfers
+ *
+ * Input Parameters:
+ *   sctrlr - SPI slave controller interface instance
+ *   data   - Command/data mode data value to be shifted out.  The width of
+ *            the data must be the same as the nbits parameter previously
+ *            provided to the bind() methods.
+ *
+ * Returned Value:
+ *   Zero if the word was successfully queue; A negated errno valid is
+ *   returned on any failure to enqueue the word (such as if the queue is
+ *   full).
+ *
+ *****************************************************************************/
+
+static int spi_enqueue(struct spi_sctrlr_s *sctrlr, FAR const void *data,
+                       size_t len)
+{
+  return 0;
+}
+
+/*****************************************************************************
+ * Name: spi_qfull
+ *
+ * Description:
+ *   Return true if the queue is full or false if there is space to add an
+ *   additional word to the queue.
+ *
+ * Input Parameters:
+ *   sctrlr - SPI slave controller interface instance
+ *
+ * Returned Value:
+ *   true if the output wueue is full
+ *
+ *****************************************************************************/
+
+static bool spi_qfull(struct spi_sctrlr_s *sctrlr)
+{
+  return false;
+}
+
+/*****************************************************************************
+ * Name: spi_qflush
+ *
+ * Description:
+ *   Discard all saved values in the output queue.  On return from this
+ *   function the output queue will be empty.  Any in-progress or otherwise
+ *   "committed" output values may not be flushed.
+ *
+ * Input Parameters:
+ *   sctrlr - SPI slave controller interface instance
+ *
+ * Returned Value:
+ *   None
+ *
+ *****************************************************************************/
+
+static void spi_qflush(struct spi_sctrlr_s *sctrlr)
+{
+  struct stm32_spidev_s *priv = (struct stm32_spidev_s *)sctrlr;
+  irqstate_t flags;
+
+  DEBUGASSERT(priv != NULL && priv->sdev != NULL);
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+  if (!priv->dmarunning)
+    {
+      return;
+    }
+#endif
+
+  /* Get exclusive access to the SPI device */
+
+  spi_lock(sctrlr, true);
+  flags = enter_critical_section();
+
+  /* Flush the input buffers */
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+  priv->ihead =
+    CONFIG_STM32H7_SPI_SLAVE_QSIZE - stm32_dmaresidual(priv->rxdma);
+#else
+  priv->ihead = 0;
+  priv->itail = 0;
+#endif
+
+  /* Flush the output buffers */
+
+  priv->ohead = 0;
+  priv->otail = 0;
+  leave_critical_section(flags);
+  spi_lock(sctrlr, false);
+}
+
+/*****************************************************************************
+ * Name: spi_rx_buffer_free
+ *
+ * Description:
+ *   Handle buffer wrapping around & invalidate the previous buffer cache
+ *
+ * Input Parameters:
+ *   ptr - pointer to the rx buffer
+ *   start - start index of just received data
+ *   end - end index of just received data
+ *
+ * Returned Value:
+ *   new start index; 0 if buffer wrapped around, end otherwise
+ *
+ *****************************************************************************/
+
+static inline int spi_rx_buffer_free(uint8_t *ptr, int start, int end)
+{
+  /* The priv->ihead can only be greater than qsize if the device driver
+   * returns garbage
+   */
+
+  if (end >= CONFIG_STM32H7_SPI_SLAVE_QSIZE)
+    {
+      end = 0;
+      up_invalidate_dcache((uintptr_t)&ptr[start],
+                           (uintptr_t)&ptr[CONFIG_STM32H7_SPI_SLAVE_QSIZE]);
+    }
+  else
+    {
+      up_invalidate_dcache((uintptr_t)&ptr[start],
+                           (uintptr_t)&ptr[end]);
+    }
+
+  return end;
+}
+
+/*****************************************************************************
+ * Name: spi_qpoll
+ *
+ * Description:
+ *   Tell the controller to output all the receive queue data.
+ *
+ * Input Parameters:
+ *   sctrlr - SPI slave controller interface instance
+ *
+ * Returned Value:
+ *   Number of bytes left in the rx queue. If the device accepted all the
+ *
+ *****************************************************************************/
+
+static size_t spi_qpoll(struct spi_sctrlr_s *sctrlr)
+{
+  struct stm32_spidev_s *priv = (struct stm32_spidev_s *)sctrlr;
+  int itail;
+  int ihead;
+  uint16_t bytes_left;
+
+  DEBUGASSERT(priv != NULL && priv->sdev != NULL);
+  DEBUGASSERT(priv->ihead < CONFIG_STM32H7_SPI_SLAVE_QSIZE);
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+  if (!priv->dmarunning)
+    {
+      return 0;
+    }
+#endif
+
+  /* Get exclusive access to the SPI device */
+
+  spi_lock(sctrlr, true);
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+  itail = CONFIG_STM32H7_SPI_SLAVE_QSIZE - stm32_dmaresidual(priv->rxdma);
+#else
+  #error Support only simplex mode rx with dma
+#endif
+
+  /* Receive all data between last read index and the current index */
+
+  ihead = priv->ihead;
+  if (ihead > itail)
+    {
+      /* Receive the end of receive buffer */
+
+      priv->ihead += SPI_SDEV_RECEIVE(priv->sdev,
+                                      (const uint16_t *)&priv->inq[ihead],
+                                      CONFIG_STM32H7_SPI_SLAVE_QSIZE -
+                                        ihead);
+
+      /* Invalidate dcache and wrap around the priv->ihead */
+
+      priv->ihead = spi_rx_buffer_free(priv->inq, ihead, priv->ihead);
+    }
+
+  ihead = priv->ihead;
+  if (ihead < itail)
+    {
+      /* Receive the data between ihead and itail */
+
+      priv->ihead += SPI_SDEV_RECEIVE(priv->sdev,
+                                      (const uint16_t *)&priv->inq[ihead],
+                                      itail - ihead);
+
+      /* Invalidate dcache and wrap around the priv->ihead */
+
+      priv->ihead = spi_rx_buffer_free(priv->inq, ihead, priv->ihead);
+    }
+
+  /* Calculate the number of bytes left in the buffer */
+
+  bytes_left = itail < priv->ihead
+    ? CONFIG_STM32H7_SPI_SLAVE_QSIZE - priv->ihead + itail
+    : itail - priv->ihead;
+
+  spi_lock(sctrlr, false);
+
+  return bytes_left;
+}
+
+/*****************************************************************************
+ * Name: spi_pm_prepare
+ *
+ * Description:
+ *   Request the driver to prepare for a new power state. This is a
+ *   warning that the system is about to enter into a new power state.  The
+ *   driver should begin whatever operations that may be required to enter
+ *   power state.  The driver may abort the state change mode by returning
+ *   a non-zero value from the callback function.
+ *
+ * Input Parameters:
+ *   cb      - Returned to the driver.  The driver version of the callback
+ *             structure may include additional, driver-specific state
+ *             data at the end of the structure.
+ *   domain  - Identifies the activity domain of the state change
+ *   pmstate - Identifies the new PM state
+ *
+ * Returned Value:
+ *   0 (OK) means the event was successfully processed and that the driver
+ *   is prepared for the PM state change.  Non-zero means that the driver
+ *   is not prepared to perform the tasks needed achieve this power setting
+ *   and will cause the state change to be aborted.  NOTE:  The prepare
+ *   method will also be recalled when reverting from lower back to higher
+ *   power consumption modes (say because another driver refused a lower
+ *   power state change).  Drivers are not permitted to return non-zero
+ *   values when reverting back to higher power consumption modes!
+ *
+ *****************************************************************************/
+
+#ifdef CONFIG_PM
+static int spi_pm_prepare(FAR struct pm_callback_s *cb, int domain,
+                          enum pm_state_e pmstate)
+{
+  struct stm32_spidev_s *priv =
+      (struct stm32_spidev_s *)((char *)cb -
+                                    offsetof(struct stm32_spidev_s, pm_cb));
+  int sval;
+
+  /* Logic to prepare for a reduced power state goes here. */
+
+  switch (pmstate)
+    {
+    case PM_NORMAL:
+    case PM_IDLE:
+      break;
+
+    case PM_STANDBY:
+    case PM_SLEEP:
+
+      /* Check if exclusive lock for SPI bus is held. */
+
+      if (nxsem_getvalue(&priv->exclsem, &sval) < 0)
+        {
+          DEBUGASSERT(false);
+          return -EINVAL;
+        }
+
+      /* If exclusive lock is held, do not allow entry to deeper PM states */
+
+      if (sval <= 0)
+        {
+          return -EBUSY;
+        }
+
+      break;
+
+    default:
+
+      /* Should not get here */
+
+      break;
+    }
+
+  return OK;
+}
+#endif
+
+/*****************************************************************************
+ * Name: spi_slave_initialize
+ *
+ * Description:
+ *   Initialize the selected SPI bus for slave operations
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *
+ * Returned Value:
+ *   None
+ *
+ *****************************************************************************/
+
+static void spi_slave_initialize(struct stm32_spidev_s *priv)
+{
+  uint32_t setbits = 0;
+  uint32_t clrbits = 0;
+#ifdef CONFIG_PM
+  int ret;
+#endif
+
+  /* Configure CR1, CFG1 and CFG2. Default configuration:
+   *   Mode 0:                        CFG2.CPHA=0 and CFG2.CPOL=0
+   *   Master:                        CFG2.MSTR=1
+   *   8-bit:                         CFG1.DSIZE=7
+   *   MSB tranmitted first:          CFG2.LSBFRST=0
+   *   Replace NSS with SSI & SSI=1:  CR1.SSI=1 CFG2.SSM=1 (prevent MODF err)
+   *   Two lines full duplex:         CFG2.COMM=0
+   */
+
+  /* CR1 */
+
+  clrbits = SPI_CR1_SPE;
+  spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, clrbits, setbits);
+
+  /* CFG1 */
+
+  clrbits = SPI_CFG1_DSIZE_MASK;
+  setbits = SPI_CFG1_DSIZE_8BIT | SPI_CFG1_FTHLV_1DATA; /* REVISIT: FTHLV */
+  spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, clrbits, setbits);
+
+  /* CFG2 */
+
+  clrbits = SPI_CFG2_CPHA | SPI_CFG2_CPOL | SPI_CFG2_LSBFRST |
+    SPI_CFG2_COMM_MASK;
+  setbits = SPI_CFG2_SSM;
+
+  switch (priv->config)
+    {
+    default:
+    case FULL_DUPLEX:
+      setbits |= SPI_CFG2_COMM_FULL;
+      break;
+    case SIMPLEX_TX:
+      setbits |= SPI_CFG2_COMM_STX;
+      break;
+    case SIMPLEX_RX:
+      setbits |= SPI_CFG2_COMM_SRX;
+      break;
+    case HALF_DUPLEX:
+      setbits |= SPI_CFG2_COMM_HALF;
+      break;
+    }
+
+  spi_modifyreg(priv, STM32_SPI_CFG2_OFFSET, clrbits, setbits);
+
+  priv->nbits     = 8;
+  priv->mode      = SPIDEV_MODE0;
+
+  /* CRCPOLY configuration */
+
+  spi_putreg(priv, STM32_SPI_CRCPOLY_OFFSET, 7);
+
+  /* Initialize the SPI semaphore that enforces mutually exclusive access. */
+
+  nxsem_init(&priv->exclsem, 0, 1);
+
+#ifdef CONFIG_STM32H7_SPI_DMA
+  /* DMA will be started in the interrupt handler, syncronized to the master
+   * nss
+   */
+
+  priv->dmarunning = false;
+
+  /* Initialize the SPI semaphores that is used to wait for DMA completion.
+   * This semaphore is used for signaling and, hence, should not have
+   * priority inheritance enabled.
+   */
+
+  nxsem_init(&priv->rxsem, 0, 0);
+  nxsem_init(&priv->txsem, 0, 0);
+
+  sem_setprotocol(&priv->rxsem, SEM_PRIO_NONE);
+  sem_setprotocol(&priv->txsem, SEM_PRIO_NONE);
+
+  if (priv->config != SIMPLEX_TX)
+    {
+      priv->rxdma = stm32_dmachannel(priv->rxch);
+      DEBUGASSERT(priv->rxdma);
+    }
+
+  if (priv->config != SIMPLEX_RX)
+    {
+      priv->txdma = stm32_dmachannel(priv->txch);
+      DEBUGASSERT(priv->txdma);
+    }
+
+#endif
+
+#ifdef CONFIG_DEBUG_SPI_INFO
+  /* Dump registers after initialization */
+
+  spi_dumpregs(priv);
+#endif
+}
+
+/*****************************************************************************
+ * Public Functions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Name: stm32_spi_slave_initialize
+ *
+ * Description:
+ *   Initialize the selected SPI port(bus) to operate as spi slave
+ *
+ * Input Parameters:
+ *   Port number (for hardware that has mutiple SPI interfaces)
+ *
+ * Returned Value:
+ *   Valid SPI device structure reference on succcess; a NULL on failure
+ *
+ *****************************************************************************/
+
+/* Helper macros to avoid duplicating all the code */
+
+#define GPIO_SPI_SCK(x) GPIO_SPI##x##_SCK
+#define GPIO_SPI_MISO(x) GPIO_SPI##x##_MISO
+#define GPIO_SPI_MOSI(x) GPIO_SPI##x##_MOSI
+#define GPIO_SPI_NSS(x) GPIO_SPI##x##_NSS
+
+#define SPI_SLAVE_INIT_BUS(x) \
+  priv = &g_spi##x##sctrlr;                                     \
+                                                                \
+  /* Only configure if the bus is not already configured */     \
+                                                                \
+  if (!priv->initialized)                                       \
+    {                                                           \
+      /* Configure SPI5 pins: SCK, MISO, MOSI and NSS */        \
+                                                                \
+      stm32_configgpio(GPIO_SPI_SCK(x));                        \
+      stm32_configgpio(GPIO_SPI_MISO(x));                       \
+      stm32_configgpio(GPIO_SPI_MOSI(x));                       \
+      priv->nss_pin = GPIO_SPI_NSS(x);                          \
+                                                                \
+      /* Set up default configuration: 8-bit, etc. */           \
+                                                                \
+      spi_slave_initialize(priv);                               \
+      priv->initialized = true;                                 \
+    }
+
+FAR struct spi_sctrlr_s *stm32_spi_slave_initialize(int bus)
+{
+  FAR struct stm32_spidev_s *priv = NULL;
+  irqstate_t flags = enter_critical_section();
+
+#ifdef CONFIG_STM32H7_SPI1_SLAVE
+  if (bus == 1)
+    {
+      SPI_SLAVE_INIT_BUS(1);
+    }
+  else
+#endif
+
+#ifdef CONFIG_STM32H7_SPI2_SLAVE
+  if (bus == 2)
+    {
+      SPI_SLAVE_INIT_BUS(2);
+    }
+  else
+#endif
+
+#ifdef CONFIG_STM32H7_SPI3_SLAVE
+  if (bus == 3)
+    {
+      SPI_SLAVE_INIT_BUS(3);
+    }
+  else
+#endif
+
+#ifdef CONFIG_STM32H7_SPI4_SLAVE
+  if (bus == 4)
+    {
+      SPI_SLAVE_INIT_BUS(4);
+    }
+  else
+#endif
+
+#ifdef CONFIG_STM32H7_SPI5_SLAVE
+  if (bus == 5)
+    {
+      SPI_SLAVE_INIT_BUS(5);
+    }
+  else
+#endif
+
+#ifdef CONFIG_STM32H7_SPI6_SLAVE
+  if (bus == 6)
+    {
+      SPI_SLAVE_INIT_BUS(6);
+    }
+  else
+#endif
+    {
+      spierr("ERROR: Unsupported SPI bus: %d\n", bus);
+      return NULL;
+    }
+
+  /* Initialize the SPI operations */
+
+  priv->sctrlr.ops = &g_sctrlr_ops;
+
+  leave_critical_section(flags);
+  return (FAR struct spi_sctrlr_s *)priv;
+}
+
+#endif /* CONFIG_STM32H7_SPI1..6_SLAVE */

Reply via email to