You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/06/28 02:38:07 UTC

[incubator-nuttx] branch master updated: Spi driver for Stm32wl55

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 4f8c8815d6 Spi driver for Stm32wl55
4f8c8815d6 is described below

commit 4f8c8815d6281b91d8183c57045af8e64285aa3b
Author: dependabot[bot] <49...@users.noreply.github.com>
AuthorDate: Mon Jun 20 02:32:51 2022 +0000

    Spi driver for Stm32wl55
    
    IRQ and DMA mode is not implemented
---
 arch/arm/src/stm32wl5/Kconfig                 |   79 ++
 arch/arm/src/stm32wl5/Make.defs               |    1 +
 arch/arm/src/stm32wl5/hardware/stm32wl5_spi.h |  277 ++++
 arch/arm/src/stm32wl5/stm32wl5.h              |   15 +
 arch/arm/src/stm32wl5/stm32wl5_spi.c          | 1859 +++++++++++++++++++++++++
 arch/arm/src/stm32wl5/stm32wl5_spi.h          |  161 +++
 6 files changed, 2392 insertions(+)

diff --git a/arch/arm/src/stm32wl5/Kconfig b/arch/arm/src/stm32wl5/Kconfig
index 750051cfdc..cce9626d1e 100644
--- a/arch/arm/src/stm32wl5/Kconfig
+++ b/arch/arm/src/stm32wl5/Kconfig
@@ -42,6 +42,8 @@ config STM32WL5_STM32WL5XXX_CPU1
 	select STM32WL5_HAVE_USART1
 	select STM32WL5_HAVE_USART2
 	select STM32WL5_HAVE_LPUART1
+	select STM32WL5_HAVE_SPI1
+	select STM32WL5_HAVE_SPI2S2
 
 
 comment "STM32WL5 Peripherals"
@@ -133,6 +135,14 @@ config STM32WL5_HAVE_LPUART1
 	bool
 	default n
 
+config STM32WL5_HAVE_SPI1
+	bool
+	default n
+
+config STM32WL5_HAVE_SPI2S2
+	bool
+	default n
+
 # These "hidden" settings are the OR of individual peripheral selections
 # indicating that the general capability is required.
 
@@ -140,6 +150,8 @@ config STM32WL5_USART
 	bool
 	default n
 
+config STM32WL5_SPI
+	bool
 
 # These are the peripheral selections proper
 
@@ -159,6 +171,12 @@ config STM32WL5_LPUART1
 	select ARCH_HAVE_SERIAL_TERMIOS
 	select STM32WL5_USART
 
+config STM32WL5_SPI2S2
+	bool "SPI2S2"
+	default n
+	depends on STM32WL5_HAVE_SPI2S2
+	select STM32WL5_SPI
+
 comment "APB2 Peripherals"
 
 config STM32WL5_SYSCFG
@@ -172,6 +190,11 @@ config STM32WL5_USART1
 	select ARCH_HAVE_SERIAL_TERMIOS
 	select STM32WL5_USART
 
+config STM32WL5_SPI1
+	bool "SPI1"
+	default n
+	depends on STM32WL5_HAVE_SPI1
+	select STM32WL5_SPI
 
 endmenu # STM32WL5 Peripheral Support
 
@@ -220,4 +243,60 @@ endchoice # LPUART1 Driver Configuration
 
 endmenu # [LP]U[S]ART Configuration
 
+menu "SPI Configuration"
+	depends on STM32WL5_SPI
+
+config STM32WL5_SPI_INTERRUPTS
+	bool "Interrupt driver SPI"
+	default n
+	---help---
+		Select to enable interrupt driven SPI support. Non-interrupt-driven,
+		poll-waiting is recommended if the interrupt rate would be to high in
+		the interrupt driven case.
+
+config STM32WL5_SPI_DMA
+	bool "SPI DMA"
+	default n
+	---help---
+		Use DMA to improve SPI transfer performance.  Cannot be used with STM32WL5_SPI_INTERRUPT.
+
+config STM32WL5_SPI1_DMA
+	bool "SPI1 DMA"
+	default n
+	depends on STM32WL5_SPI1 && STM32WL5_SPI_DMA
+	---help---
+		Use DMA to improve SPI1 transfer performance.
+
+config STM32WL5_SPI1_DMA_BUFFER
+	int "SPI1 DMA buffer size"
+	default 0
+	depends on STM32WL5_SPI1_DMA
+	---help---
+		Add a properly aligned DMA buffer for RX and TX DMA for SPI1.
+
+config STM32WL5_SPI_DMATHRESHOLD
+	int "SPI DMA threshold"
+	default 4
+	depends on STM32WL5_SPI_DMA
+	---help---
+		When SPI DMA is enabled, small DMA transfers will still be performed
+		by polling logic.  But we need a threshold value to determine what
+		is small.
+
+config STM32WL5_SPI2S2_DMA
+	bool "SPI2S2 DMA"
+	default n
+	depends on STM32WL5_SPI2S2 && STM32WL5_SPI_DMA
+	---help---
+		Use DMA to improve SPI2S2 transfer performance.
+
+config STM32WL5_SPI2S2_DMA_BUFFER
+	int "SPI2S2 DMA buffer size"
+	default 0
+	depends on STM32WL5_SPI2S2_DMA
+	---help---
+		Add a properly aligned DMA buffer for RX and TX DMA for SPI2S2.
+
+endmenu # SPI Configuration
+
 endif # ARCH_CHIP_STM32WL5
diff --git a/arch/arm/src/stm32wl5/Make.defs b/arch/arm/src/stm32wl5/Make.defs
index b7917433a2..9397b10f63 100644
--- a/arch/arm/src/stm32wl5/Make.defs
+++ b/arch/arm/src/stm32wl5/Make.defs
@@ -32,3 +32,4 @@ CHIP_CSRCS += stm32wl5_irq.c stm32wl5_lowputc.c stm32wl5_rcc.c
 CHIP_CSRCS += stm32wl5_serial.c stm32wl5_start.c stm32wl5_waste.c stm32wl5_uid.c
 CHIP_CSRCS += stm32wl5_lse.c stm32wl5_lsi.c stm32wl5_idle.c
 CHIP_CSRCS += stm32wl5_pwr.c stm32wl5_tim.c stm32wl5_flash.c stm32wl5_timerisr.c
+CHIP_CSRCS += stm32wl5_spi.c
diff --git a/arch/arm/src/stm32wl5/hardware/stm32wl5_spi.h b/arch/arm/src/stm32wl5/hardware/stm32wl5_spi.h
new file mode 100644
index 0000000000..415518ac49
--- /dev/null
+++ b/arch/arm/src/stm32wl5/hardware/stm32wl5_spi.h
@@ -0,0 +1,277 @@
+/****************************************************************************
+ * arch/arm/src/stm32wl5/hardware/stm32wl5_spi.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WL5_HARDWARE_STM32WL5_SPI_H
+#define __ARCH_ARM_SRC_STM32WL5_HARDWARE_STM32WL5_SPI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* SPI version **************************************************************/
+
+/* SPI IP v1 is default unless v2 or greater is specified for this chip */
+
+#undef HAVE_SPI_I2S               /* No I2S mode in the SPI peripheral */
+#undef HAVE_SPI_I2S_ASTRT         /* No I2S asynchronous start capability */
+#undef HAVE_SPI_TI_MODE           /* Motorola frame mode only; no TI mode */
+#undef HAVE_SPI_ARB_DATA_SIZE     /* Data size 8 or 16 bit; not arbitrary 4-16 bit */
+#undef HAVE_SPI_FIFOS             /* No Tx/Rx FIFOs */
+#undef HAVE_SPI_NSSP              /* No NSS Pulse Management in master mode */
+
+#if defined(STM32WL5_HAVE_IP_SPI_V2)
+#  define HAVE_SPI_I2S            /* Some SPI peripherals have I2S mode */
+#  undef HAVE_SPI_I2S_ASTRT       /* No I2S asynchronous start capability */
+#  define HAVE_SPI_TI_MODE        /* Have Motorola and TI frame modes */
+#  undef HAVE_SPI_ARB_DATA_SIZE   /* Data size 8 or 16 bit; not arbitrary 4-16 bit */
+#  undef HAVE_SPI_FIFOS           /* No Tx/Rx FIFOs */
+#  undef HAVE_SPI_NSSP            /* No NSS Pulse Management in master mode */
+#endif
+
+#if defined(STM32WL5_HAVE_IP_SPI_V3)
+#  define HAVE_SPI_I2S            /* Some SPI peripherals have I2S mode */
+#  undef HAVE_SPI_I2S_ASTRT       /* No I2S asynchronous start capability */
+#  define HAVE_SPI_TI_MODE        /* Have Motorola and TI frame modes */
+#  define HAVE_SPI_ARB_DATA_SIZE  /* Supports arbitrary data size from 4-16 bits */
+#  define HAVE_SPI_FIFOS          /* Have Tx/Rx FIFOs */
+#  undef HAVE_SPI_NSSP            /* No NSS Pulse Management in master mode */
+#endif
+
+#if defined(STM32WL5_HAVE_IP_SPI_V4)
+#  define HAVE_SPI_I2S            /* Some SPI peripherals have I2S mode */
+#  define HAVE_SPI_I2S_ASTRT      /* Supports I2S asynchronous start capability */
+#  define HAVE_SPI_TI_MODE        /* Have Motorola and TI frame modes */
+#  define HAVE_SPI_ARB_DATA_SIZE  /* Supports arbitrary data size from 4-16 bits */
+#  define HAVE_SPI_FIFOS          /* Have Tx/Rx FIFOs */
+#  define HAVE_SPI_NSSP           /* Have NSS Pulse Management in master mode */
+#endif
+
+/* Maximum allowed speed as per specifications for all SPIs */
+
+#if defined(CONFIG_STM32WL5_STM32F4XXX)
+#  define STM32WL5_SPI_CLK_MAX     37500000UL
+#else
+#  define STM32WL5_SPI_CLK_MAX     18000000UL
+#endif
+
+/* Register Offsets *********************************************************/
+
+#define STM32WL5_SPI_CR1_OFFSET       0x0000  /* SPI Control Register 1 (16-bit) */
+#define STM32WL5_SPI_CR2_OFFSET       0x0004  /* SPI control register 2 (16-bit) */
+#define STM32WL5_SPI_SR_OFFSET        0x0008  /* SPI status register (16-bit) */
+#define STM32WL5_SPI_DR_OFFSET        0x000c  /* SPI data register (16-bit) */
+#define STM32WL5_SPI_CRCPR_OFFSET     0x0010  /* SPI CRC polynomial register (16-bit) */
+#define STM32WL5_SPI_RXCRCR_OFFSET    0x0014  /* SPI Rx CRC register (16-bit) */
+#define STM32WL5_SPI_TXCRCR_OFFSET    0x0018  /* SPI Tx CRC register (16-bit) */
+
+#if defined(HAVE_SPI_I2S)
+#  define STM32WL5_SPI_I2SCFGR_OFFSET 0x001c  /* I2S configuration register */
+#  define STM32WL5_SPI_I2SPR_OFFSET   0x0020  /* I2S prescaler register */
+#endif
+
+/* Register Addresses *******************************************************/
+
+#if STM32WL5_NSPI > 0
+#  define STM32WL5_SPI1_CR1          \
+  (STM32WL5_SPI1_BASE + STM32WL5_SPI_CR1_OFFSET)
+#  define STM32WL5_SPI1_CR2          \
+  (STM32WL5_SPI1_BASE + STM32WL5_SPI_CR2_OFFSET)
+#  define STM32WL5_SPI1_SR           \
+  (STM32WL5_SPI1_BASE + STM32WL5_SPI_SR_OFFSET)
+#  define STM32WL5_SPI1_DR           \
+  (STM32WL5_SPI1_BASE + STM32WL5_SPI_DR_OFFSET)
+#  define STM32WL5_SPI1_CRCPR        \
+  (STM32WL5_SPI1_BASE + STM32WL5_SPI_CRCPR_OFFSET)
+#  define STM32WL5_SPI1_RXCRCR       \
+  (STM32WL5_SPI1_BASE + STM32WL5_SPI_RXCRCR_OFFSET)
+#  define STM32WL5_SPI1_TXCRCR       \
+  (STM32WL5_SPI1_BASE + STM32WL5_SPI_TXCRCR_OFFSET)
+#endif
+
+#if STM32WL5_NSPI > 1
+#  define STM32WL5_SPI2S2_CR1        \
+  (STM32WL5_SPI2S2_BASE + STM32WL5_SPI_CR1_OFFSET)
+#  define STM32WL5_SPI2S2_CR2        \
+  (STM32WL5_SPI2S2_BASE + STM32WL5_SPI_CR2_OFFSET)
+#  define STM32WL5_SPI2S2_SR         \
+  (STM32WL5_SPI2S2_BASE + STM32WL5_SPI_SR_OFFSET)
+#  define STM32WL5_SPI2S2_DR         \
+  (STM32WL5_SPI2S2_BASE + STM32WL5_SPI_DR_OFFSET)
+#  define STM32WL5_SPI2S2_CRCPR      \
+  (STM32WL5_SPI2S2_BASE + STM32WL5_SPI_CRCPR_OFFSET)
+#  define STM32WL5_SPI2S2_RXCRCR     \
+  (STM32WL5_SPI2S2_BASE + STM32WL5_SPI_RXCRCR_OFFSET)
+#  define STM32WL5_SPI2S2_TXCRCR     \
+  (STM32WL5_SPI2S2_BASE + STM32WL5_SPI_TXCRCR_OFFSET)
+#  if defined(HAVE_SPI_I2S)
+#    define STM32WL5_SPI2S2_I2SCFGR  \
+  (STM32WL5_SPI2S2_BASE + STM32WL5_SPI_I2SCFGR_OFFSET)
+#    define STM32WL5_SPI2S2_I2SPR    \
+  (STM32WL5_SPI2S2_BASE + STM32WL5_SPI_I2SPR_OFFSET)
+#  endif
+#endif
+
+/* Register Bitfield Definitions ********************************************/
+
+/* SPI Control Register 1 */
+
+#define SPI_CR1_CPHA              (1 << 0)                /* Bit 0: Clock Phase */
+#define SPI_CR1_CPOL              (1 << 1)                /* Bit 1: Clock Polarity */
+#define SPI_CR1_MSTR              (1 << 2)                /* Bit 2: Master Selection */
+#define SPI_CR1_BR_SHIFT          (3)                     /* Bits 5:3 Baud Rate Control */
+#define SPI_CR1_BR_MASK           (7 << SPI_CR1_BR_SHIFT)
+#define SPI_CR1_FPCLCKd2          (0 << SPI_CR1_BR_SHIFT) /* 000: fPCLK/2 */
+#define SPI_CR1_FPCLCKd4          (1 << SPI_CR1_BR_SHIFT) /* 001: fPCLK/4 */
+#define SPI_CR1_FPCLCKd8          (2 << SPI_CR1_BR_SHIFT) /* 010: fPCLK/8 */
+#define SPI_CR1_FPCLCKd16         (3 << SPI_CR1_BR_SHIFT) /* 011: fPCLK/16 */
+#define SPI_CR1_FPCLCKd32         (4 << SPI_CR1_BR_SHIFT) /* 100: fPCLK/32 */
+#define SPI_CR1_FPCLCKd64         (5 << SPI_CR1_BR_SHIFT) /* 101: fPCLK/64 */
+#define SPI_CR1_FPCLCKd128        (6 << SPI_CR1_BR_SHIFT) /* 110: fPCLK/128 */
+#define SPI_CR1_FPCLCKd256        (7 << SPI_CR1_BR_SHIFT) /* 111: fPCLK/256 */
+#define SPI_CR1_SPE               (1 << 6)                /* Bit 6: SPI Enable */
+#define SPI_CR1_LSBFIRST          (1 << 7)                /* Bit 7: Frame Format */
+#define SPI_CR1_SSI               (1 << 8)                /* Bit 8: Internal slave select */
+#define SPI_CR1_SSM               (1 << 9)                /* Bit 9: Software slave management */
+#define SPI_CR1_RXONLY            (1 << 10)               /* Bit 10: Receive only */
+#define SPI_CR1_CRCL              (1 << 11)               /* Bit 11: CRC length */
+#define SPI_CR1_CRCNEXT           (1 << 12)               /* Bit 12: Transmit CRC next */
+#define SPI_CR1_CRCEN             (1 << 13)               /* Bit 13: Hardware CRC calculation enable */
+#define SPI_CR1_BIDIOE            (1 << 14)               /* Bit 14: Output enable in bidirectional mode */
+#define SPI_CR1_BIDIMODE          (1 << 15)               /* Bit 15: Bidirectional data mode enable */
+
+/* SPI Control Register 2 */
+
+#define SPI_CR2_RXDMAEN           (1 << 0)  /* Bit 0: Rx Buffer DMA Enable */
+#define SPI_CR2_TXDMAEN           (1 << 1)  /* Bit 1: Tx Buffer DMA Enable */
+#define SPI_CR2_SSOE              (1 << 2)  /* Bit 2: SS Output Enable */
+
+#if defined(HAVE_SPI_NSSP)
+#  define SPI_CR2_NSSP            (1 << 3)  /* Bit 3: NSS Pulse Management (Master mode only) */
+#endif
+
+#if defined(HAVE_SPI_TI_MODE)
+#  define SPI_CR2_FRF             (1 << 4)  /* Bit 4: Frame format: 0=Motorola, 1=TI */
+#endif
+
+#define SPI_CR2_ERRIE             (1 << 5)  /* Bit 5: Error interrupt enable */
+#define SPI_CR2_RXNEIE            (1 << 6)  /* Bit 6: RX buffer not empty interrupt enable */
+#define SPI_CR2_TXEIE             (1 << 7)  /* Bit 7: Tx buffer empty interrupt enable */
+
+#define SPI_CR2_DS_SHIFT          (8)       /* Bits 8-11:  Data size */
+#define SPI_CR2_DS_MASK           (15 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS(n)         ((uint32_t)((n) - 1) << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_4BIT       (3 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_5BIT       (4 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_6BIT       (5 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_7BIT       (6 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_8BIT       (7 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_9BIT       (8 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_10BIT      (9 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_11BIT      (10 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_12BIT      (11 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_13BIT      (12 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_14BIT      (13 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_15BIT      (14 << SPI_CR2_DS_SHIFT)
+#    define SPI_CR2_DS_16BIT      (15 << SPI_CR2_DS_SHIFT)
+#define SPI_CR2_FRXTH             (1 << 12) /* Bit 12: FIFO reception threshold */
+#define SPI_CR2_LDMARX            (1 << 13) /* Bit 13: Last DMA transfer for reception */
+#define SPI_CR2_LDMATX            (1 << 14) /* Bit 14: Last DMA transfer for transmission */
+
+/* SPI status register */
+
+#define SPI_SR_RXNE               (1 << 0)  /* Bit 0: Receive buffer not empty */
+#define SPI_SR_TXE                (1 << 1)  /* Bit 1: Transmit buffer empty */
+
+#if defined(HAVE_SPI_I2S)
+#  define SPI_SR_CHSIDE           (1 << 2)  /* Bit 2: Channel side */
+#  define SPI_SR_UDR              (1 << 3)  /* Bit 3: Underrun flag */
+#endif
+
+#define SPI_SR_CRCERR             (1 << 4)  /* Bit 4: CRC error flag */
+#define SPI_SR_MODF               (1 << 5)  /* Bit 5: Mode fault */
+#define SPI_SR_OVR                (1 << 6)  /* Bit 6: Overrun flag */
+#define SPI_SR_BSY                (1 << 7)  /* Bit 7: Busy flag */
+
+#if defined(HAVE_SPI_I2S) || defined(HAVE_SPI_TI_MODE)
+#  define SPI_SR_FRE              (1 << 8)  /* Bit 8: TI frame format error */
+#endif
+
+#if defined(HAVE_SPI_FIFOS)
+#  define SPI_SR_FRLVL_SHIFT      (9)                       /* Bits 9-10: FIFO reception level */
+#  define SPI_SR_FRLVL_MASK       (3 << SPI_SR_FRLVL_SHIFT)
+#    define SPI_SR_FRLVL_EMPTY    (0 << SPI_SR_FRLVL_SHIFT) /* FIFO empty */
+#    define SPI_SR_FRLVL_QUARTER  (1 << SPI_SR_FRLVL_SHIFT) /* 1/4 FIFO */
+#    define SPI_SR_FRLVL_HALF     (2 << SPI_SR_FRLVL_SHIFT) /* 1/2 FIFO */
+#    define SPI_SR_FRLVL_FULL     (3 << SPI_SR_FRLVL_SHIFT) /* FIFO full */
+#  define SPI_SR_FTLVL_SHIFT      (11)                      /* Bits 11-12: FIFO transmission level */
+#  define SPI_SR_FTLVL_MASK       (3 << SPI_SR_FTLVL_SHIFT)
+#    define SPI_SR_FTLVL_EMPTY    (0 << SPI_SR_FTLVL_SHIFT) /* FIFO empty */
+#    define SPI_SR_FTLVL_QUARTER  (1 << SPI_SR_FTLVL_SHIFT) /* 1/4 FIFO */
+#    define SPI_SR_FTLVL_HALF     (2 << SPI_SR_FTLVL_SHIFT) /* 1/2 FIFO */
+#    define SPI_SR_FTLVL_FULL     (3 << SPI_SR_FTLVL_SHIFT) /* FIFO full */
+#endif
+
+/* I2S configuration register */
+
+#if defined(HAVE_SPI_I2S)
+#  define SPI_I2SCFGR_CHLEN          (1 << 0)                               /* Bit 0: Channel length (number of bits per audio channel) */
+#  define SPI_I2SCFGR_DATLEN_SHIFT   (1)                                    /* Bit 1-2: Data length to be transferred */
+#  define SPI_I2SCFGR_DATLEN_MASK    (3 << SPI_I2SCFGR_DATLEN_SHIFT)
+#    define SPI_I2SCFGR_DATLEN_16BIT (0 << SPI_I2SCFGR_DATLEN_SHIFT)        /* 00: 16-bit data length */
+#    define SPI_I2SCFGR_DATLEN_8BIT  (1 << SPI_I2SCFGR_DATLEN_SHIFT)        /* 01: 24-bit data length */
+#    define SPI_I2SCFGR_DATLEN_32BIT (2 << SPI_I2SCFGR_DATLEN_SHIFT)        /* 10: 32-bit data length */
+#  define SPI_I2SCFGR_CKPOL          (1 << 3)                               /* Bit 3: Steady state clock polarity */
+#  define SPI_I2SCFGR_I2SSTD_SHIFT   (4)                                    /* Bit 4-5: I2S standard selection */
+#  define SPI_I2SCFGR_I2SSTD_MASK    (3 << SPI_I2SCFGR_I2SSTD_SHIFT)
+#    define SPI_I2SCFGR_I2SSTD_PHILLIPS    (0 << SPI_I2SCFGR_I2SSTD_SHIFT)  /* 00: I2S Phillips standard. */
+#    define SPI_I2SCFGR_I2SSTD_MSB   (1 << SPI_I2SCFGR_I2SSTD_SHIFT)        /* 01: MSB justified standard (left justified) */
+#    define SPI_I2SCFGR_I2SSTD_LSB   (2 << SPI_I2SCFGR_I2SSTD_SHIFT)        /* 10: LSB justified standard (right justified) */
+#    define SPI_I2SCFGR_I2SSTD_PCM   (3 << SPI_I2SCFGR_I2SSTD_SHIFT)        /* 11: PCM standard */
+#  define SPI_I2SCFGR_PCMSYNC        (1 << 7)                               /* Bit 7: PCM frame synchronization */
+#  define SPI_I2SCFGR_I2SCFG_SHIFT   (8)                                    /* Bit 8-9: I2S configuration mode */
+#  define SPI_I2SCFGR_I2SCFG_MASK    (3 << SPI_I2SCFGR_I2SCFG_SHIFT)
+#    define SPI_I2SCFGR_I2SCFG_STX   (0 << SPI_I2SCFGR_I2SCFG_SHIFT)        /* 00: Slave - transmit */
+#    define SPI_I2SCFGR_I2SCFG_SRX   (1 << SPI_I2SCFGR_I2SCFG_SHIFT)        /* 01: Slave - receive */
+#    define SPI_I2SCFGR_I2SCFG_MTX   (2 << SPI_I2SCFGR_I2SCFG_SHIFT)        /* 10: Master - transmit */
+#    define SPI_I2SCFGR_I2SCFG_MRX   (3 << SPI_I2SCFGR_I2SCFG_SHIFT)        /* 11: Master - receive */
+#  define SPI_I2SCFGR_I2SE           (1 << 10)                              /* Bit 10: I2S Enable */
+#  define SPI_I2SCFGR_I2SMOD         (1 << 11)                              /* Bit 11: I2S mode selection */
+#  if defined(HAVE_SPI_I2S_ASTRT)
+#    define SPI_I2SCFGR_ASTRTEN      (1 << 12)                              /* Bit 12: Asynchronous start enable */
+#  endif
+#endif
+
+/* I2S prescaler register */
+
+#if defined(HAVE_SPI_I2S)
+#  define SPI_I2SPR_I2SDIV_SHIFT     (0)       /* Bit 0-7: I2S Linear prescaler */
+#  define SPI_I2SPR_I2SDIV_MASK      (0xff << SPI_I2SPR_I2SDIV_SHIFT)
+#  define SPI_I2SPR_ODD              (1 << 8)  /* Bit 8: Odd factor for the prescaler */
+#  define SPI_I2SPR_MCKOE            (1 << 9)  /* Bit 9: Master clock output enable */
+#endif
+
+#endif /* __ARCH_ARM_SRC_STM32WL5_HARDWARE_STM32WL5_SPI_H */
diff --git a/arch/arm/src/stm32wl5/stm32wl5.h b/arch/arm/src/stm32wl5/stm32wl5.h
index b819b71ffb..7407096f50 100644
--- a/arch/arm/src/stm32wl5/stm32wl5.h
+++ b/arch/arm/src/stm32wl5/stm32wl5.h
@@ -40,6 +40,7 @@
 #include "stm32wl5_lowputc.h"
 #include "stm32wl5_pwr.h"
 #include "stm32wl5_rcc.h"
+#include "stm32wl5_spi.h"
 #include "stm32wl5_tim.h"
 #include "stm32wl5_uart.h"
 
@@ -47,4 +48,18 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wl5_spidev_initialize
+ *
+ * Description:
+ *   Called to configure SPI chip select GPIO pins.
+ *
+ ****************************************************************************/
+
+void stm32wl5_spidev_initialize(void);
+
 #endif /* __ARCH_ARM_SRC_STM32WL5_STM32WL5_H */
diff --git a/arch/arm/src/stm32wl5/stm32wl5_spi.c b/arch/arm/src/stm32wl5/stm32wl5_spi.c
new file mode 100644
index 0000000000..a338678d57
--- /dev/null
+++ b/arch/arm/src/stm32wl5/stm32wl5_spi.c
@@ -0,0 +1,1859 @@
+/****************************************************************************
+ * arch/arm/src/stm32wl5/stm32wl5_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wl5_spi1/2select and stm32wl5_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).
+ * All other methods (including stm32wl5_spibus_initialize())  are provided
+ * by common STM32 logic.  To use this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wl5_boardinitialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wl5_spi1/2select() and stm32wl5_spi1/2() functions
+ *      in your board-specific logic.  These functions will perform chip
+ *      selection and status operations using GPIOs in the way your board is
+ *      configured.
+ *   3. Add a calls to stm32wl5_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wl5_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wl5.h"
+#include "stm32wl5_gpio.h"
+#ifdef CONFIG_STM32WL5_SPI_DMA
+#include "stm32wl5_dma.h"
+#endif
+#include "stm32wl5_spi.h"
+
+#if defined(CONFIG_STM32WL5_SPI1) || defined(CONFIG_STM32WL5_SPI2S2)
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WL5_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+#  error "DMA driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WL5_SPI_INTERRUPTS) && defined(CONFIG_STM32WL5_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  elif defined(CONFIG_STM32WL5_STM32WL5XXX_CPU1
+#warning "Verify, read doc and Implement"
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  else
+#    error "Unknown STM32WL5 DMA"
+#  endif
+
+#  if defined(CONFIG_STM32WL5_STM32WL5XXX_CPU1)
+#    if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#      error "Illegal value for CONFIG_SPI_DMAPRIO"
+#    endif
+#  else
+#    error "Unknown STM32 DMA"
+#  endif
+
+/* DMA channel configuration */
+
+#if defined(CONFIG_STM32WL5_STM32WL5XXX_CPU1)
+#  define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#  define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#  define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#  define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#  define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#  define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#  define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#  define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+#else
+#  error "Unknown STM32WL5 DMA"
+#endif
+
+#  define SPIDMA_BUFFER_MASK   (0x03)
+#  define SPIDMA_SIZE(b) (((b) + SPIDMA_BUFFER_MASK) & ~SPIDMA_BUFFER_MASK)
+#  define SPIDMA_BUF_ALIGN   aligned_data(4)
+
+#  if defined(CONFIG_STM32WL5_SPI1_DMA_BUFFER) && \
+            CONFIG_STM32WL5_SPI1_DMA_BUFFER > 0
+#    define SPI1_DMABUFSIZE_ADJUSTED SPIDMA_SIZE(CONFIG_STM32WL5_SPI1_DMA_BUFFER)
+#    define SPI1_DMABUFSIZE_ALGN SPIDMA_BUF_ALIGN
+#  endif
+
+#  if defined(CONFIG_STM32WL5_SPI2S2_DMA_BUFFER) && \
+            CONFIG_STM32WL5_SPI2S2_DMA_BUFFER > 0
+#    define SPI2S2_DMABUFSIZE_ADJUSTED SPIDMA_SIZE(CONFIG_STM32WL5_SPI2S2_DMA_BUFFER)
+#    define SPI2S2_DMABUFSIZE_ALGN SPIDMA_BUF_ALIGN
+#  endif
+
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wl5_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WL5_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WL5_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint8_t          rxch;         /* The RX DMA channel number */
+  uint8_t          txch;         /* The TX DMA channel number */
+  uint8_t          *rxbuf;       /* The RX DMA buffer */
+  uint8_t          *txbuf;       /* The TX DMA buffer */
+  size_t           buflen;       /* The DMA buffer length */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wl5_spidev_s *priv,
+                                  uint8_t offset);
+
+static inline uint8_t spi_getreg8(struct stm32wl5_spidev_s *priv,
+                                  uint8_t offset);
+
+static inline void spi_putreg(struct stm32wl5_spidev_s *priv,
+                              uint8_t offset,
+                              uint16_t value);
+
+static inline void spi_putreg8(struct stm32wl5_spidev_s *priv,
+                               uint8_t offset,
+                               uint8_t value);
+
+static inline uint16_t spi_readword(struct stm32wl5_spidev_s *priv);
+static inline void spi_writeword(struct stm32wl5_spidev_s *priv,
+                                 uint16_t byte);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static int         spi_dmarxwait(struct stm32wl5_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wl5_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wl5_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wl5_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wl5_spidev_s *priv,
+                                  void *rxbuffer,
+                                  void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wl5_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy,
+                                  size_t nwords);
+static inline void spi_dmarxstart(struct stm32wl5_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wl5_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer,
+                                 size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wl5_spidev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI1
+static const struct spi_ops_s g_sp1iops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wl5_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wl5_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wl5_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wl5_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+#if defined(SPI1_DMABUFSIZE_ADJUSTED)
+static uint8_t g_spi1_txbuf[SPI1_DMABUFSIZE_ADJUSTED] SPI1_DMABUFSIZE_ALGN;
+static uint8_t g_spi1_rxbuf[SPI1_DMABUFSIZE_ADJUSTED] SPI1_DMABUFSIZE_ALGN;
+#endif
+
+static struct stm32wl5_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_sp1iops
+    },
+  .spibase  = STM32WL5_SPI1_BASE,
+  .spiclock = STM32WL5_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WL5_SPI_INTERRUPTS
+  .spiirq   = STM32WL5_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WL5_SPI_DMA
+#  ifdef CONFIG_STM32WL5_SPI1_DMA
+  .rxch     = DMACHAN_SPI1_RX,
+  .txch     = DMACHAN_SPI1_TX,
+#if defined(SPI1_DMABUFSIZE_ADJUSTED)
+  .rxbuf    = g_spi1_rxbuf,
+  .txbuf    = g_spi1_txbuf,
+  .buflen   = SPI1_DMABUFSIZE_ADJUSTED,
+#    endif
+#  else
+  .rxch     = 0,
+  .txch     = 0,
+#  endif
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WL5_SPI2S2
+static const struct spi_ops_s g_sp2iops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wl5_spi2s2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wl5_spi2s2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wl5_spi2s2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wl5_s2register,  /* provided externally */
+#else
+  .registercallback  = 0,                   /* not implemented */
+#endif
+};
+
+#if defined(SPI2S2_DMABUFSIZE_ADJUSTED)
+static uint8_t g_spi2s2_txbuf[SPI2S2_DMABUFSIZE_ADJUSTED]
+  SPI2S2_DMABUFSIZE_ALGN;
+static uint8_t g_spi2s2_rxbuf[SPI2S2_DMABUFSIZE_ADJUSTED]
+  SPI2S2_DMABUFSIZE_ALGN;
+#endif
+
+static struct stm32wl5_spidev_s g_spi2s2dev =
+{
+  .spidev   =
+    {
+      &g_sp2iops
+    },
+  .spibase  = STM32WL5_SPI2S2_BASE,
+  .spiclock = STM32WL5_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WL5_SPI_INTERRUPTS
+  .spiirq   = STM32WL5_IRQ_SPI2S2,
+#endif
+#ifdef CONFIG_STM32WL5_SPI_DMA
+#  ifdef CONFIG_STM32WL5_SPI2S2_DMA
+  .rxch     = DMACHAN_SPI2S2_RX,
+  .txch     = DMACHAN_SPI2S2_TX,
+#if defined(SPI2S2_DMABUFSIZE_ADJUSTED)
+  .rxbuf    = g_spi2s2_rxbuf,
+  .txbuf    = g_spi2s2_txbuf,
+  .buflen   = SPI2S2_DMABUFSIZE_ADJUSTED,
+#    endif
+#  else
+  .rxch     = 0,
+  .txch     = 0,
+#  endif
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wl5_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wl5_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wl5_spidev_s *priv,
+                              uint8_t offset,
+                              uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write an 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wl5_spidev_s *priv,
+                               uint8_t offset,
+                               uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wl5_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WL5_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0)
+    {
+    }
+
+  /* Then return the received byte */
+
+  /* "When the data frame size fits into one byte
+   * (less than or equal to 8 bits),
+   *  data packing is used automatically when any read or write 16-bit access
+   *  is performed on the SPIx_DR register. The double data frame pattern is
+   *  handled in parallel in this case. At first, the SPI operates using the
+   *  pattern stored in the LSB of the accessed word, then with the other
+   *  half stored in the MSB.... The receiver then has to access both data
+   *  frames by a single 16-bit read of SPIx_DR as a response to this single
+   *  RXNE event. The RxFIFO threshold setting and the following read access
+   *  must be always kept aligned at the receiver side, as data can be lost
+   *  if it is not in line."
+   */
+
+  if (priv->nbits < 9)
+    {
+      return (uint16_t)spi_getreg8(priv, STM32WL5_SPI_DR_OFFSET);
+    }
+  else
+    {
+      return spi_getreg(priv, STM32WL5_SPI_DR_OFFSET);
+    }
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one word or byte to SPI. If the frame size is 8 bit or lower
+ *   a byte is written. In other case a word is written.
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   word - word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wl5_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WL5_SPI_SR_OFFSET) & SPI_SR_TXE) == 0)
+    {
+    }
+
+  /* Then send the word */
+
+  if (priv->nbits < 9)
+    {
+      spi_putreg8(priv, STM32WL5_SPI_DR_OFFSET, (uint8_t)word);
+    }
+  else
+    {
+      spi_putreg(priv, STM32WL5_SPI_DR_OFFSET, word);
+    }
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static int spi_dmarxwait(struct stm32wl5_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret != OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static int spi_dmatxwait(struct stm32wl5_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->txsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->txresult == 0 && ret != OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static inline void spi_dmarxwakeup(struct stm32wl5_spidev_s *priv)
+{
+  nxsem_post(&priv->rxsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static inline void spi_dmatxwakeup(struct stm32wl5_spidev_s *priv)
+{
+  nxsem_post(&priv->txsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->rxresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmarxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->txresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmatxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxsetup
+ *
+ * Description:
+ *   Setup to perform RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static void spi_dmarxsetup(struct stm32wl5_spidev_s *priv,
+                           void *rxbuffer,
+                           void *rxdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (priv->nbits > 8)
+    {
+      /* 16-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA16_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA8_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Configure the RX DMA */
+
+  stm32wl5_dmasetup(priv->rxdma, priv->spibase + STM32WL5_SPI_DR_OFFSET,
+                    (uint32_t)rxbuffer, nwords, priv->rxccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxsetup
+ *
+ * Description:
+ *   Setup to perform TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static void spi_dmatxsetup(struct stm32wl5_spidev_s *priv,
+                           const void *txbuffer,
+                           const void *txdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (priv->nbits > 8)
+    {
+      /* 16-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA16_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA8_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Setup the TX DMA */
+
+  stm32wl5_dmasetup(priv->txdma, priv->spibase + STM32WL5_SPI_DR_OFFSET,
+                    (uint32_t)txbuffer, nwords, priv->txccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxstart
+ *
+ * Description:
+ *   Start RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static inline void spi_dmarxstart(struct stm32wl5_spidev_s *priv)
+{
+  priv->rxresult = 0;
+  stm32wl5_dmastart(priv->rxdma, spi_dmarxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxstart
+ *
+ * Description:
+ *   Start TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static inline void spi_dmatxstart(struct stm32wl5_spidev_s *priv)
+{
+  priv->txresult = 0;
+  stm32wl5_dmastart(priv->txdma, spi_dmatxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_modifycr1
+ *
+ * Description:
+ *   Clear and set bits in the CR1 register
+ *
+ * Input Parameters:
+ *   priv    - Device-specific state data
+ *   clrbits - The bits to clear
+ *   setbits - The bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_modifycr1(struct stm32wl5_spidev_s *priv,
+                          uint16_t setbits,
+                          uint16_t clrbits)
+{
+  uint16_t cr1;
+  cr1 = spi_getreg(priv, STM32WL5_SPI_CR1_OFFSET);
+  cr1 &= ~clrbits;
+  cr1 |= setbits;
+  spi_putreg(priv, STM32WL5_SPI_CR1_OFFSET, cr1);
+
+  spiinfo("CR1 (0x%lx) = 0x%04x\n", priv->spibase + STM32WL5_SPI_CR1_OFFSET,
+          cr1);
+}
+
+/****************************************************************************
+ * Name: spi_modifycr2
+ *
+ * Description:
+ *   Clear and set bits in the CR2 register
+ *
+ * Input Parameters:
+ *   priv    - Device-specific state data
+ *   clrbits - The bits to clear
+ *   setbits - The bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_modifycr2(struct stm32wl5_spidev_s *priv, uint16_t setbits,
+                          uint16_t clrbits)
+{
+  uint16_t cr2;
+  cr2  = spi_getreg(priv, STM32WL5_SPI_CR2_OFFSET);
+  cr2 &= ~clrbits;
+  cr2 |= setbits;
+  spi_putreg(priv, STM32WL5_SPI_CR2_OFFSET, cr2);
+  spiinfo("CR2 (0x%lx) = 0x%04x\n", priv->spibase + STM32WL5_SPI_CR2_OFFSET,
+          cr2);
+}
+
+/****************************************************************************
+ * Name: spi_lock
+ *
+ * Description:
+ *   On SPI buses where there are multiple devices, it will be necessary to
+ *   lock SPI to have exclusive access to the buses for a sequence of
+ *   transfers.  The bus should be locked before the chip is selected. After
+ *   locking the SPI bus, the caller should then also call the setfrequency,
+ *   setbits, and setmode methods to make sure that the SPI is properly
+ *   configured for the device.  If the SPI bus is being shared, then it
+ *   may have been left in an incompatible state.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   lock - true: Lock spi bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  int ret;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev -       Device-specific state data
+ *   frequency - The SPI frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32_t spi_setfrequency(struct spi_dev_s *dev,
+                                 uint32_t frequency)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  uint16_t setbits;
+  uint32_t actual;
+
+  /* Has the frequency changed? */
+
+  if (frequency != priv->frequency)
+    {
+      /* Choices are limited by PCLK frequency with a set of divisors */
+
+      if (frequency >= priv->spiclock >> 1)
+        {
+          /* More than fPCLK/2.  This is as fast as we can go */
+
+          setbits = SPI_CR1_FPCLCKd2; /* 000: fPCLK/2 */
+          actual = priv->spiclock >> 1;
+        }
+      else if (frequency >= priv->spiclock >> 2)
+        {
+          /* Between fPCLCK/2 and fPCLCK/4, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd4; /* 001: fPCLK/4 */
+          actual = priv->spiclock >> 2;
+        }
+      else if (frequency >= priv->spiclock >> 3)
+        {
+          /* Between fPCLCK/4 and fPCLCK/8, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd8; /* 010: fPCLK/8 */
+          actual = priv->spiclock >> 3;
+        }
+      else if (frequency >= priv->spiclock >> 4)
+        {
+          /* Between fPCLCK/8 and fPCLCK/16, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd16; /* 011: fPCLK/16 */
+          actual = priv->spiclock >> 4;
+        }
+      else if (frequency >= priv->spiclock >> 5)
+        {
+          /* Between fPCLCK/16 and fPCLCK/32, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd32; /* 100: fPCLK/32 */
+          actual = priv->spiclock >> 5;
+        }
+      else if (frequency >= priv->spiclock >> 6)
+        {
+          /* Between fPCLCK/32 and fPCLCK/64, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd64; /*  101: fPCLK/64 */
+          actual = priv->spiclock >> 6;
+        }
+      else if (frequency >= priv->spiclock >> 7)
+        {
+          /* Between fPCLCK/64 and fPCLCK/128, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd128; /* 110: fPCLK/128 */
+          actual = priv->spiclock >> 7;
+        }
+      else
+        {
+          /* Less than fPCLK/128.  This is as slow as we can go */
+
+          setbits = SPI_CR1_FPCLCKd256; /* 111: fPCLK/256 */
+          actual = priv->spiclock >> 8;
+        }
+
+      spi_modifycr1(priv, 0, SPI_CR1_SPE);
+      spi_modifycr1(priv, setbits, SPI_CR1_BR_MASK);
+      spi_modifycr1(priv, SPI_CR1_SPE, 0);
+
+      /* Save the frequency selection so that subsequent reconfigurations
+       * will be faster.
+       */
+
+      spiinfo("Frequency %" PRIu32 "->%" PRIu32 "\n", frequency, actual);
+
+      priv->frequency = frequency;
+      priv->actual    = actual;
+    }
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.  see enum spi_mode_e for mode definitions
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The SPI mode requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      /* Yes... Set CR1 appropriately */
+
+      switch (mode)
+        {
+        case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+          setbits = 0;
+          clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+          setbits = SPI_CR1_CPHA;
+          clrbits = SPI_CR1_CPOL;
+          break;
+
+        case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+          setbits = SPI_CR1_CPOL;
+          clrbits = SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+          setbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          clrbits = 0;
+          break;
+
+#ifdef SPI_CR2_FRF    /* If MCU supports TI Synchronous Serial Frame Format */
+        case SPIDEV_MODETI:
+          setbits = 0;
+          clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          break;
+#endif
+
+        default:
+          return;
+        }
+
+        spi_modifycr1(priv, 0, SPI_CR1_SPE);
+        spi_modifycr1(priv, setbits, clrbits);
+        spi_modifycr1(priv, SPI_CR1_SPE, 0);
+
+#ifdef SPI_CR2_FRF    /* If MCU supports TI Synchronous Serial Frame Format */
+      switch (mode)
+        {
+          case SPIDEV_MODE0:
+          case SPIDEV_MODE1:
+          case SPIDEV_MODE2:
+          case SPIDEV_MODE3:
+            setbits = 0;
+            clrbits = SPI_CR2_FRF;
+            break;
+
+          case SPIDEV_MODETI:
+            setbits = SPI_CR2_FRF;
+            clrbits = 0;
+            break;
+
+          default:
+            return;
+        }
+
+      spi_modifycr1(priv, 0, SPI_CR1_SPE);
+      spi_modifycr2(priv, setbits, clrbits);
+      spi_modifycr1(priv, SPI_CR1_SPE, 0);
+#endif
+
+      /* Save the mode so that subsequent re-configurations will be
+       * faster
+       */
+
+      priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: spi_setbits
+ *
+ * Description:
+ *   Set the number of bits per word.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *   nbits - The number of bits requested
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  /* Has the number of bits changed? */
+
+  if (nbits != priv->nbits)
+    {
+      if (nbits < 4 || nbits > 16)
+        {
+          spierr("ERROR: nbits out of range: %d. Supported range [4..16].\n",
+                 nbits);
+          return;
+        }
+
+      spiinfo("nbits=%d (previous val %d)\n", nbits, priv->nbits);
+
+      clrbits = SPI_CR2_DS_MASK;
+      setbits = SPI_CR2_DS(nbits);
+
+      /* If nbits is <=8, then we are in byte mode and FRXTH must be set
+       * (else, transaction will not complete).
+       */
+
+      if (nbits < 9)
+        {
+          setbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 1 byte */
+        }
+      else
+        {
+          clrbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 2 bytes */
+        }
+
+      spi_modifycr1(priv, 0, SPI_CR1_SPE);
+      spi_modifycr2(priv, setbits, clrbits);
+      spi_modifycr1(priv, SPI_CR1_SPE, 0);
+      /* Save the selection so that subsequent re-configurations will be
+       * faster.
+       */
+
+      priv->nbits = nbits;
+    }
+  else
+    {
+      spiinfo("nbits=%d (CR2 has proper value, no need to update)\n", nbits);
+    }
+}
+
+/****************************************************************************
+ * Name: spi_hwfeatures
+ *
+ * Description:
+ *   Set hardware-specific feature flags.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   features - H/W feature flags
+ *
+ * Returned Value:
+ *   Zero (OK) if the selected H/W features are enabled; A negated errno
+ *   value if any H/W feature is not supportable.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_HWFEATURES
+static int spi_hwfeatures(struct spi_dev_s *dev,
+                          spi_hwfeatures_t features)
+{
+#if defined(CONFIG_SPI_BITORDER) || defined(CONFIG_SPI_TRIGGER)
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+#endif
+
+#ifdef CONFIG_SPI_BITORDER
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("features=%08x\n", features);
+
+  /* Transfer data LSB first? */
+
+  if ((features & HWFEAT_LSBFIRST) != 0)
+    {
+      setbits = SPI_CR1_LSBFIRST;
+      clrbits = 0;
+    }
+  else
+    {
+      setbits = 0;
+      clrbits = SPI_CR1_LSBFIRST;
+    }
+
+  spi_modifycr1(priv, 0, SPI_CR1_SPE);
+  spi_modifycr1(priv, setbits, clrbits);
+  spi_modifycr1(priv, SPI_CR1_SPE, 0);
+
+  features &= ~HWFEAT_LSBFIRST;
+#endif
+
+#ifdef CONFIG_SPI_TRIGGER
+/* Turn deferred trigger mode on or off.  Only applicable for DMA mode. If a
+ * transfer is deferred then the DMA will not actually be triggered until a
+ * subsequent call to SPI_TRIGGER to set it off. The thread will be waiting
+ * on the transfer completing as normal.
+ */
+
+  priv->defertrig = ((features & HWFEAT_TRIGGER) != 0);
+  features &= ~HWFEAT_TRIGGER;
+#endif
+
+  /* Other H/W features are not supported */
+
+  return (features == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_send
+ *
+ * Description:
+ *   Exchange one word on SPI
+ *
+ * Input Parameters:
+ *   dev - Device-specific state data
+ *   wd  - The word to send.  the size of the data is determined by the
+ *         number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   response
+ *
+ ****************************************************************************/
+
+static uint32_t spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  uint32_t regval;
+  uint32_t ret;
+
+  DEBUGASSERT(priv && priv->spibase);
+
+  spi_writeword(priv, (uint32_t)(wd & 0xffff));
+  ret = (uint32_t)spi_readword(priv);
+
+  /* Check and clear any error flags
+   * (Reading from the SR clears the error flags)
+   */
+
+  regval = spi_getreg(priv, STM32WL5_SPI_SR_OFFSET);
+
+  spiinfo("Sent: %04" PRIx32 " Return: %04" PRIx32
+          " Status: %02" PRIx32 "\n", wd, ret, regval);
+  UNUSED(regval);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_exchange (no DMA).  aka spi_exchange_nodma
+ *
+ * Description:
+ *   Exchange a block of data on SPI without using DMA
+ *
+ *   REVISIT:
+ *   This function could be much more efficient by exploiting (1) RX and TX
+ *   FIFOs and (2) the STM32 F3 data packing.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to a buffer in which to receive data
+ *   nwords   - the length of data to be exchanged in units of words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_STM32WL5_SPI_DMA) || defined(CONFIG_STM32WL5_DMACAPABLE) || \
+     defined(CONFIG_STM32WL5_SPI_DMATHRESHOLD)
+#if !defined(CONFIG_STM32WL5_SPI_DMA)
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+#else
+static void spi_exchange_nodma(struct spi_dev_s *dev,
+                               const void *txbuffer,
+                               void *rxbuffer, size_t nwords)
+#endif
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  DEBUGASSERT(priv && priv->spibase);
+
+  spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
+
+  /* 8- or 16-bit mode? */
+
+  if (priv->nbits > 8)
+    {
+      /* 16-bit mode */
+
+      const uint16_t *src  = (const uint16_t *)txbuffer;
+      uint16_t *dest = (uint16_t *)rxbuffer;
+      uint16_t  word;
+
+      while (nwords-- > 0)
+        {
+          /* Get the next word to write.  Is there a source buffer? */
+
+          if (src)
+            {
+              word = *src++;
+            }
+          else
+            {
+              word = 0xffff;
+            }
+
+          /* Exchange one word */
+
+          word = (uint16_t)spi_send(dev, (uint32_t)word);
+
+          /* Is there a buffer to receive the return value? */
+
+          if (dest)
+            {
+              *dest++ = word;
+            }
+        }
+    }
+  else
+    {
+      /* 8-bit mode */
+
+      const uint8_t *src  = (const uint8_t *)txbuffer;
+      uint8_t *dest = (uint8_t *)rxbuffer;
+      uint8_t  word;
+
+      while (nwords-- > 0)
+        {
+          /* Get the next word to write.  Is there a source buffer? */
+
+          if (src)
+            {
+              word = *src++;
+            }
+          else
+            {
+              word = 0xff;
+            }
+
+          /* Exchange one word */
+
+          word = (uint8_t)spi_send(dev, (uint32_t)word);
+
+          /* Is there a buffer to receive the return value? */
+
+          if (dest)
+            {
+              *dest++ = word;
+            }
+        }
+    }
+}
+#endif /* !CONFIG_STM32WL5_SPI_DMA || CONFIG_STM32WL5_DMACAPABLE || CONFIG_STM32WL5_SPI_DMATHRESHOLD */
+
+/****************************************************************************
+ * Name: spi_exchange (with DMA capability)
+ *
+ * Description:
+ *   Exchange a block of data on SPI using DMA
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to a buffer in which to receive data
+ *   nwords   - the length of data to be exchanged in units of words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+{
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+  void *xbuffer = rxbuffer;
+  int ret;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Convert the number of word to a number of bytes */
+
+  size_t nbytes = (priv->nbits > 8) ? nwords << 1 : nwords;
+
+#ifdef CONFIG_STM32WL5_SPI_DMATHRESHOLD
+  /* If this is a small SPI transfer, then let spi_exchange_nodma() do the
+   * work.
+   */
+
+  if (nbytes <= CONFIG_STM32WL5_SPI_DMATHRESHOLD)
+    {
+      spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
+      return;
+    }
+#endif
+
+  if ((priv->rxdma == NULL) || (priv->txdma == NULL) ||
+      up_interrupt_context())
+    {
+      /* Invalid DMA channels, or interrupt context, fall
+       * back to non-DMA method.
+       */
+
+      spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
+      return;
+    }
+
+#ifdef CONFIG_STM32WL5_DMACAPABLE
+  if ((txbuffer != NULL && priv->txbuf == NULL &&
+      !stm32wl5_dmacapable((uintptr_t)txbuffer, nwords, priv->txccr)) ||
+      (rxbuffer != NULL && priv->rxbuf == NULL &&
+       !stm32wl5_dmacapable((uintptr_t)rxbuffer, nwords, priv->rxccr)))
+    {
+      /* Unsupported memory region fall back to non-DMA method. */
+
+      spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
+    }
+  else
+#endif
+    {
+      static uint16_t rxdummy = 0xffff;
+      static const uint16_t txdummy = 0xffff;
+
+      spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n",
+               txbuffer, rxbuffer, nwords);
+      DEBUGASSERT(priv && priv->spibase);
+
+      /* Setup DMAs */
+
+      /* If this bus uses a in driver buffers we will incur 2 copies,
+       * The copy cost is << less the non DMA transfer time and having
+       * the buffer in the driver ensures DMA can be used. This is because
+       * the API does not support passing the buffer extent so the only
+       * extent is buffer + the transfer size. These can sizes be less than
+       * the cache line size, and not aligned and typically greater then 4
+       * bytes, which is about the break even point for the DMA IO overhead.
+       */
+
+      if (txbuffer != NULL && priv->txbuf != NULL)
+        {
+          if (nbytes > priv->buflen)
+            {
+              nbytes = priv->buflen;
+            }
+
+          memcpy(priv->txbuf, txbuffer, nbytes);
+          txbuffer = priv->txbuf;
+          rxbuffer = rxbuffer == NULL ? priv->rxbuf : rxbuffer;
+        }
+
+      spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords);
+      spi_dmatxsetup(priv, txbuffer, &txdummy, nwords);
+
+#ifdef CONFIG_SPI_TRIGGER
+      /* Is deferred triggering in effect? */
+
+      if (!priv->defertrig)
+        {
+          /* No.. Start the DMAs */
+
+          spi_dmarxstart(priv);
+          spi_dmatxstart(priv);
+        }
+      else
+        {
+          /* Yes.. indicated that we are ready to be started */
+
+          priv->trigarmed = true;
+        }
+#else
+      /* Start the DMAs */
+
+      spi_dmarxstart(priv);
+      spi_dmatxstart(priv);
+#endif
+
+      /* Then wait for each to complete */
+
+      ret = spi_dmarxwait(priv);
+      if (ret == OK)
+        {
+          ret = spi_dmatxwait(priv);
+        }
+
+      if (rxbuffer != NULL && priv->rxbuf != NULL && ret == OK)
+        {
+          memcpy(xbuffer, priv->rxbuf, nbytes);
+        }
+
+#ifdef CONFIG_SPI_TRIGGER
+      priv->trigarmed = false;
+#endif
+    }
+}
+#endif /* CONFIG_STM32WL5_SPI_DMA */
+
+/****************************************************************************
+ * Name: spi_trigger
+ *
+ * Description:
+ *   Trigger a previously configured DMA transfer.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *
+ * Returned Value:
+ *   OK       - Trigger was fired
+ *   ENOTSUP  - Trigger not fired due to lack of DMA support
+ *   EIO      - Trigger not fired because not previously primed
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_TRIGGER
+static int spi_trigger(struct spi_dev_s *dev)
+{
+#ifdef CONFIG_STM32WL5_SPI_DMA
+  struct stm32wl5_spidev_s *priv = (struct stm32wl5_spidev_s *)dev;
+
+  if (!priv->trigarmed)
+    {
+      return -EIO;
+    }
+
+  spi_dmarxstart(priv);
+  spi_dmatxstart(priv);
+
+  return OK;
+#else
+  return -ENOSYS;
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_sndblock
+ *
+ * Description:
+ *   Send a block of data on SPI
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   nwords   - the length of data to send from the buffer in number of
+ *              words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SPI_EXCHANGE
+static void spi_sndblock(struct spi_dev_s *dev,
+                         const void *txbuffer,
+                         size_t nwords)
+{
+  spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords);
+  return spi_exchange(dev, txbuffer, NULL, nwords);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_recvblock
+ *
+ * Description:
+ *   Receive a block of data from SPI
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   rxbuffer - A pointer to the buffer in which to receive data
+ *   nwords   - the length of data that can be received in the buffer in
+ *              number of words.  The wordsize is determined by the number
+ *              of bits-per-word selected for the SPI interface.  If
+ *              nbits <= 8, the data is packed into uint8_t's; if nbits >8,
+ *              the data is packed into uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SPI_EXCHANGE
+static void spi_recvblock(struct spi_dev_s *dev,
+                          void *rxbuffer,
+                          size_t nwords)
+{
+  spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords);
+  return spi_exchange(dev, NULL, rxbuffer, nwords);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_bus_initialize
+ *
+ * Description:
+ *   Initialize the selected SPI bus in its default state (Master, 8-bit,
+ *   mode 0, etc.)
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_bus_initialize(struct stm32wl5_spidev_s *priv)
+{
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  /* Configure CR1 and CR2. Default configuration:
+   *   Mode 0:                        CR1.CPHA=0 and CR1.CPOL=0
+   *   Master:                        CR1.MSTR=1
+   *   8-bit:                         CR2.DS=7
+   *   MSB transmitted first:         CR1.LSBFIRST=0
+   *   Replace NSS with SSI & SSI=1:  CR1.SSI=1 CR1.SSM=1
+   *                               (prevents MODF error, NSS pin is not used)
+   *   Two lines full duplex:         CR1.BIDIMODE=0 CR1.BIDIOIE=(Don't care)
+   *                                  and CR1.RXONLY=0
+   */
+
+  clrbits = SPI_CR1_CPHA | SPI_CR1_CPOL | SPI_CR1_BR_MASK |
+            SPI_CR1_LSBFIRST | SPI_CR1_RXONLY | SPI_CR1_CRCL |
+            SPI_CR1_BIDIOE | SPI_CR1_BIDIMODE;
+  setbits = SPI_CR1_MSTR | SPI_CR1_SSI | SPI_CR1_SSM;
+  spi_modifycr1(priv, setbits, clrbits);
+
+  clrbits = SPI_CR2_DS_MASK;
+  setbits = SPI_CR2_DS_8BIT | SPI_CR2_FRXTH; /* FRXTH must be high in 8-bit mode */
+  spi_modifycr2(priv, setbits, clrbits);
+
+  priv->frequency = 0;
+  priv->nbits     = 8;
+  priv->mode      = SPIDEV_MODE0;
+
+  /* Select a default frequency of approx. 400KHz */
+
+  spi_setfrequency((struct spi_dev_s *)priv, 400000);
+
+  /* CRCPOLY configuration */
+
+  spi_putreg(priv, STM32WL5_SPI_CRCPR_OFFSET, 7);
+
+  /* Initialize the SPI semaphore that enforces mutually exclusive access */
+
+  nxsem_init(&priv->exclsem, 0, 1);
+
+#ifdef CONFIG_STM32WL5_SPI_DMA
+  /* 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.
+   */
+
+  if (priv->rxch && priv->txch)
+    {
+      nxsem_init(&priv->rxsem, 0, 0);
+      nxsem_init(&priv->txsem, 0, 0);
+
+      nxsem_set_protocol(&priv->rxsem, SEM_PRIO_NONE);
+      nxsem_set_protocol(&priv->txsem, SEM_PRIO_NONE);
+
+      /* Get DMA channels.  NOTE: stm32wl5_dmachannel() will always assign
+       * the DMA channel.  If the channel is not available, then
+       * stm32wl5_dmachannel() will block and wait until the channel becomes
+       * available.
+       * WARNING: If you have another device sharing a DMA channel with
+       * SPI and the code never releases that channel, then the call to
+       * stm32wl5_dmachannel()  will hang forever in this function!
+       *  Don't let your design do that!
+       */
+
+      priv->rxdma = stm32wl5_dmachannel(priv->rxch);
+      priv->txdma = stm32wl5_dmachannel(priv->txch);
+      DEBUGASSERT(priv->rxdma && priv->txdma);
+
+      spi_modifycr2(priv, SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN, 0);
+    }
+  else
+    {
+      priv->rxdma = NULL;
+      priv->txdma = NULL;
+    }
+#endif
+
+  /* Enable SPI */
+
+  spi_modifycr1(priv, SPI_CR1_SPE, 0);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wl5_spibus_initialize
+ *
+ * Description:
+ *   Initialize the selected SPI bus
+ *
+ * Input Parameters:
+ *   Port number (for hardware that has multiple SPI interfaces)
+ *
+ * Returned Value:
+ *   Valid SPI device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct spi_dev_s *stm32wl5_spibus_initialize(int bus)
+{
+  struct stm32wl5_spidev_s *priv = NULL;
+
+  irqstate_t flags = enter_critical_section();
+
+#ifdef CONFIG_STM32WL5_SPI1
+  if (bus == 1)
+    {
+      /* Select SPI1 */
+
+      priv = &g_spi1dev;
+
+      /* Only configure if the bus is not already configured */
+
+      if (!priv->initialized)
+        {
+          /* Configure SPI1 pins: SCK, MISO, and MOSI */
+
+          stm32wl5_configgpio(GPIO_SPI1_SCK);
+          stm32wl5_configgpio(GPIO_SPI1_MISO);
+          stm32wl5_configgpio(GPIO_SPI1_MOSI);
+
+          /* Set up default configuration: Master, 8-bit, etc. */
+
+          spi_bus_initialize(priv);
+          priv->initialized = true;
+        }
+    }
+  else
+#endif
+#ifdef CONFIG_STM32WL5_SPI2S2
+  if (bus == 2)
+    {
+      /* Select SPI2S2 */
+
+      priv = &g_spi2s2dev;
+
+      /* Only configure if the bus is not already configured */
+
+      if (!priv->initialized)
+        {
+          /* Configure SPI2S2 pins: SCK, MISO, and MOSI */
+
+          stm32wl5_configgpio(GPIO_SPI2S2_SCK);
+          stm32wl5_configgpio(GPIO_SPI2S2_MISO);
+          stm32wl5_configgpio(GPIO_SPI2S2_MOSI);
+
+          /* Set up default configuration: Master, 8-bit, etc. */
+
+          spi_bus_initialize(priv);
+          priv->initialized = true;
+        }
+    }
+  else
+#endif
+    {
+      spierr("ERROR: Unsupported SPI bus: %d\n", bus);
+    }
+
+  leave_critical_section(flags);
+  return (struct spi_dev_s *)priv;
+}
+
+#endif /* CONFIG_STM32WL5_SPI1 || CONFIG_STM32WL5_SPI2S2 */
diff --git a/arch/arm/src/stm32wl5/stm32wl5_spi.h b/arch/arm/src/stm32wl5/stm32wl5_spi.h
new file mode 100644
index 0000000000..af6fd854cf
--- /dev/null
+++ b/arch/arm/src/stm32wl5/stm32wl5_spi.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+ * arch/arm/src/stm32wl5/stm32wl5_spi.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WL5_STM32WL5_SPI_H
+#define __ARCH_ARM_SRC_STM32WL5_STM32WL5_SPI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "hardware/stm32wl5_spi.h"
+
+#include <nuttx/config.h>
+
+#include <nuttx/spi/spi.h>
+
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+struct spi_dev_s;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wl5_spibus_initialize
+ *
+ * Description:
+ *   Initialize the selected SPI bus
+ *
+ * Input Parameters:
+ *   bus number (for hardware that has multiple SPI interfaces)
+ *
+ * Returned Value:
+ *   Valid SPI device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct spi_dev_s *stm32wl5_spibus_initialize(int bus);
+
+/****************************************************************************
+ * Name:  stm32wl5_spi1/2select and stm32wl5_spi1/2status
+ *
+ * Description:
+ *   The external functions, stm32wl5_spi1/2select, stm32wl5_spi1/2status,
+ *   and stm32wl5_spi1/2cmddata must be provided by board-specific logic.
+ *   These are implementations of the select, status, and cmddata methods of
+ *   the SPI interface defined by struct spi_ops_s (see
+ *   include/nuttx/spi/spi.h). All other methods (including
+ *   stm32wl5_spibus_initialize()) are provided by common STM32 logic.
+ *   To use this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wl5_boardinitialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wl5_spi1/2select() and stm32wl5_spi1/2status()
+ *      functions in your board-specific logic.  These functions will
+ *      perform chip selection and status operations using GPIOs in the way
+ *      your board is configured.
+ *   3. If CONFIG_SPI_CMDDATA is defined in your NuttX configuration file,
+ *      then provide stm32wl5_spi1/2cmddata() functions in your board-
+ *      specific logic. These functions will perform cmd/data selection
+ *      operations using GPIOs in the way your board is configured.
+ *   4. Add a calls to stm32wl5_spibus_initialize() in your low level
+ *      application initialization logic
+ *   5. The handle returned by stm32wl5_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WL5_SPI1
+void stm32wl5_spi1select(struct spi_dev_s *dev, uint32_t devid,
+                         bool selected);
+uint8_t stm32wl5_spi1status(struct spi_dev_s *dev, uint32_t devid);
+int stm32wl5_spi1cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd);
+#endif
+
+#ifdef CONFIG_STM32WL5_SPI2S2
+void stm32wl5_spi2s2select(struct spi_dev_s *dev, uint32_t devid,
+                           bool selected);
+uint8_t stm32wl5_spi2s2status(struct spi_dev_s *dev, uint32_t devid);
+int stm32wl5_spi2s2cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd);
+#endif
+
+/****************************************************************************
+ * Name: stm32wl5_spi1/2s2register
+ *
+ * Description:
+ *   If the board supports a card detect callback to inform the SPI-based
+ *   MMC/SD driver when an SD card is inserted or removed, then
+ *   CONFIG_SPI_CALLBACK should be defined and the following function(s)
+ *   must be implemented.  These functions implements the registercallback
+ *   method of the SPI interface (see include/nuttx/spi/spi.h for details)
+ *
+ * Input Parameters:
+ *   dev -      Device-specific state data
+ *   callback - The function to call on the media change
+ *   arg -      A caller provided value to return with the callback
+ *
+ * Returned Value:
+ *   0 on success; negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_CALLBACK
+#ifdef CONFIG_STM32WL5_SPI1
+int stm32wl5_spi1register(struct spi_dev_s *dev, spi_mediachange_t callback,
+                          void *arg);
+#endif
+
+#ifdef CONFIG_STM32WL5_SPI2S2
+int stm32wl5_spi2s2register(struct spi_dev_s *dev,
+                            spi_mediachange_t callback,
+                            void *arg);
+#endif
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_ARM_SRC_STM32WL5_STM32WL5_SPI_H */