You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by da...@apache.org on 2020/05/28 12:14:53 UTC
[incubator-nuttx] 04/04: arch/arm/src/stm32h7: Add the spi slave
bus control driver
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 <ju...@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 <ju...@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 */