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