This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 9e8c81cf4c2 arm/nrf54l: add SPIM support
9e8c81cf4c2 is described below
commit 9e8c81cf4c21310b7091347c9600d59272f10a31
Author: raiden00pl <[email protected]>
AuthorDate: Sun Sep 20 16:22:51 2026 +0200
arm/nrf54l: add SPIM support
arm/nrf54l: add SPIM support
Assisted-by: Codex:GPT-6
Signed-off-by: raiden00pl <[email protected]>
---
Documentation/platforms/arm/nrf54l/index.rst | 16 +-
arch/arm/src/nrf54l/CMakeLists.txt | 3 +
arch/arm/src/nrf54l/Kconfig | 60 +
arch/arm/src/nrf54l/Make.defs | 4 +
arch/arm/src/nrf54l/hardware/nrf54l_spi.h | 193 +++
arch/arm/src/nrf54l/nrf54l_spi.c | 1640 ++++++++++++++++++++++++++
arch/arm/src/nrf54l/nrf54l_spi.h | 191 +++
7 files changed, 2106 insertions(+), 1 deletion(-)
diff --git a/Documentation/platforms/arm/nrf54l/index.rst
b/Documentation/platforms/arm/nrf54l/index.rst
index 1c7340e263d..b7318e976f9 100644
--- a/Documentation/platforms/arm/nrf54l/index.rst
+++ b/Documentation/platforms/arm/nrf54l/index.rst
@@ -22,7 +22,7 @@ QDEC No
RADIO Yes Bluetooth LE through SDC
RRAMC Yes Progmem erase/write interface
SAADC Yes
-SPIM No
+SPIM Yes
TIMER Yes
TWIM No
UARTE Yes No hardware flow control
@@ -98,6 +98,20 @@ outputs and pulls disconnected before opening the device.
AIN0 through
AIN7 map to P1.04/05/06/07/11/12/13/14 on L15 and
P1.00/31/30/29/06/05/04/03 on LM20.
+SPIM
+----
+
+SPI0 through SPI4 correspond to SPIM20, SPIM21, SPIM22, SPIM30 and SPIM00.
+nRF54LM20 also provides SPI5 and SPI6 using SPIM23 and SPIM24. Each bus
+requires ``BOARD_SPIn_SCK_PIN`` and the applicable ``BOARD_SPIn_MOSI_PIN``
+and ``BOARD_SPIn_MISO_PIN`` definitions. Boards provide the select, status
+and optional command/data callbacks declared in ``nrf54l_spi.h``.
+
+The driver supports 8-bit transfers, modes 0 through 3 and configurable
+bit order. SPI4 supports up to 32 MHz; other instances support up to 8 MHz.
+``SPI_SETFREQUENCY()`` returns the clock selected using the hardware divider.
+SPI and UART cannot use the same SERIAL instance concurrently.
+
TIMER
-----
diff --git a/arch/arm/src/nrf54l/CMakeLists.txt
b/arch/arm/src/nrf54l/CMakeLists.txt
index 2ca9e701030..0cbf364e664 100644
--- a/arch/arm/src/nrf54l/CMakeLists.txt
+++ b/arch/arm/src/nrf54l/CMakeLists.txt
@@ -46,6 +46,9 @@ endif()
if(CONFIG_NRF54L_UART)
list(APPEND SRCS nrf54l_serial.c)
endif()
+if(CONFIG_NRF54L_SPI_MASTER)
+ list(APPEND SRCS nrf54l_spi.c)
+endif()
if(CONFIG_NRF54L_SAADC)
list(APPEND SRCS nrf54l_adc.c)
endif()
diff --git a/arch/arm/src/nrf54l/Kconfig b/arch/arm/src/nrf54l/Kconfig
index e74892745b6..81e5ce4277b 100644
--- a/arch/arm/src/nrf54l/Kconfig
+++ b/arch/arm/src/nrf54l/Kconfig
@@ -25,6 +25,12 @@ endchoice # nRF54L Chip Selection
# Peripheral Selection
+config NRF54L_SPI_MASTER
+ bool
+ default n
+ select SPI
+ select ARCH_HAVE_SPI_BITORDER
+
config NRF54L_UART
bool
default n
@@ -43,6 +49,48 @@ config NRF54L_GPIOTE
bool "GPIOTE (GPIO interrupts)"
default n
+config NRF54L_SPI0_MASTER
+ bool "SPI0 Master (SPIM20)"
+ default n
+ depends on !NRF54L_UART0
+ select NRF54L_SPI_MASTER
+
+config NRF54L_SPI1_MASTER
+ bool "SPI1 Master (SPIM21)"
+ default n
+ depends on !NRF54L_UART1
+ select NRF54L_SPI_MASTER
+
+config NRF54L_SPI2_MASTER
+ bool "SPI2 Master (SPIM22)"
+ default n
+ depends on !NRF54L_UART2
+ select NRF54L_SPI_MASTER
+
+config NRF54L_SPI3_MASTER
+ bool "SPI3 Master (SPIM30)"
+ default n
+ depends on !NRF54L_UART3
+ select NRF54L_SPI_MASTER
+
+config NRF54L_SPI4_MASTER
+ bool "SPI4 Master (SPIM00)"
+ default n
+ depends on !NRF54L_UART4
+ select NRF54L_SPI_MASTER
+
+config NRF54L_SPI5_MASTER
+ bool "SPI5 Master (SPIM23)"
+ default n
+ depends on !ARCH_CHIP_NRF54L15 && !NRF54L_UART5
+ select NRF54L_SPI_MASTER
+
+config NRF54L_SPI6_MASTER
+ bool "SPI6 Master (SPIM24)"
+ default n
+ depends on !ARCH_CHIP_NRF54L15 && !NRF54L_UART6
+ select NRF54L_SPI_MASTER
+
config NRF54L_UART0
bool "UART0"
default n
@@ -436,6 +484,18 @@ endif # NRF54L_SAADC
endmenu # SAADC Configuration
+menu "SPI Configuration"
+
+if NRF54L_SPI_MASTER
+
+config NRF54L_SPI_MASTER_INTERRUPTS
+ bool "SPI Master interrupts support"
+ default n
+
+endif # NRF54L_SPI_MASTER
+
+endmenu # SPI Configuration
+
menuconfig NRF54L_SOFTDEVICE_CONTROLLER
bool "SoftDevice Controller"
depends on ALLOW_BSDNORDIC_COMPONENTS
diff --git a/arch/arm/src/nrf54l/Make.defs b/arch/arm/src/nrf54l/Make.defs
index 0654b99c67d..85565492eca 100644
--- a/arch/arm/src/nrf54l/Make.defs
+++ b/arch/arm/src/nrf54l/Make.defs
@@ -46,6 +46,10 @@ ifeq ($(CONFIG_NRF54L_UART),y)
CHIP_CSRCS += nrf54l_serial.c
endif
+ifeq ($(CONFIG_NRF54L_SPI_MASTER),y)
+CHIP_CSRCS += nrf54l_spi.c
+endif
+
ifeq ($(CONFIG_NRF54L_SAADC),y)
CHIP_CSRCS += nrf54l_adc.c
endif
diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_spi.h
b/arch/arm/src/nrf54l/hardware/nrf54l_spi.h
new file mode 100644
index 00000000000..c512d28c671
--- /dev/null
+++ b/arch/arm/src/nrf54l/hardware/nrf54l_spi.h
@@ -0,0 +1,193 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/hardware/nrf54l_spi.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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_NRF54L_HARDWARE_NRF54L_SPI_H
+#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_SPI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/nrf54l_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets for SPI master (SPIM) ***********************************/
+
+#define NRF54L_SPIM_TASK_START_OFFSET 0x0000
/* Start SPI transaction */
+#define NRF54L_SPIM_TASK_STOP_OFFSET 0x0004
/* Stop SPI transaction */
+#define NRF54L_SPIM_TASK_SUSPEND_OFFSET 0x000c
/* Suspend SPI transaction */
+#define NRF54L_SPIM_TASK_RESUME_OFFSET 0x0010
/* Resume SPI transaction */
+#define NRF54L_SPIM_TASK_DMA_RX_ENABLEMATCH_OFFSET(n) (0x0028 + 4 * (n))
/* Enable RX match event n */
+#define NRF54L_SPIM_TASK_DMA_RX_DISABLEMATCH_OFFSET(n) (0x0038 + 4 * (n))
/* Disable RX match event n */
+#define NRF54L_SPIM_SUBSCRIBE_START_OFFSET 0x0080
/* Subscribe to START task */
+#define NRF54L_SPIM_SUBSCRIBE_STOP_OFFSET 0x0084
/* Subscribe to STOP task */
+#define NRF54L_SPIM_SUBSCRIBE_SUSPEND_OFFSET 0x008c
/* Subscribe to SUSPEND task */
+#define NRF54L_SPIM_SUBSCRIBE_RESUME_OFFSET 0x0090
/* Subscribe to RESUME task */
+#define NRF54L_SPIM_SUBSCRIBE_DMA_RX_ENABLEMATCH_OFFSET(n) (0x00a8 + 4 * (n))
/* Subscribe to RX ENABLEMATCH task n */
+#define NRF54L_SPIM_SUBSCRIBE_DMA_RX_DISABLEMATCH_OFFSET(n) (0x00b8 + 4 * (n))
/* Subscribe to RX DISABLEMATCH task n */
+#define NRF54L_SPIM_EVENTS_STARTED_OFFSET 0x0100
/* Transaction started */
+#define NRF54L_SPIM_EVENTS_STOPPED_OFFSET 0x0104
/* Transaction stopped */
+#define NRF54L_SPIM_EVENTS_END_OFFSET 0x0108
/* RX and TX complete */
+#define NRF54L_SPIM_EVENTS_DMA_RX_END_OFFSET 0x014c
/* RX DMA buffer completed */
+#define NRF54L_SPIM_EVENTS_DMA_RX_READY_OFFSET 0x0150
/* RX DMA buffer prepared */
+#define NRF54L_SPIM_EVENTS_DMA_RX_BUSERROR_OFFSET 0x0154
/* RX DMA bus error */
+#define NRF54L_SPIM_EVENTS_DMA_RX_MATCH_OFFSET(n) (0x0158 + 4 * (n))
/* RX data matched candidate n */
+#define NRF54L_SPIM_EVENTS_DMA_TX_END_OFFSET 0x0168
/* TX DMA buffer completed */
+#define NRF54L_SPIM_EVENTS_DMA_TX_READY_OFFSET 0x016c
/* TX DMA buffer prepared */
+#define NRF54L_SPIM_EVENTS_DMA_TX_BUSERROR_OFFSET 0x0170
/* TX DMA bus error */
+#define NRF54L_SPIM_PUBLISH_STARTED_OFFSET 0x0180
/* Publish STARTED event */
+#define NRF54L_SPIM_PUBLISH_STOPPED_OFFSET 0x0184
/* Publish STOPPED event */
+#define NRF54L_SPIM_PUBLISH_END_OFFSET 0x0188
/* Publish END event */
+#define NRF54L_SPIM_PUBLISH_DMA_RX_END_OFFSET 0x01cc
/* Publish RX END event */
+#define NRF54L_SPIM_PUBLISH_DMA_RX_READY_OFFSET 0x01d0
/* Publish RX READY event */
+#define NRF54L_SPIM_PUBLISH_DMA_RX_BUSERROR_OFFSET 0x01d4
/* Publish RX BUSERROR event */
+#define NRF54L_SPIM_PUBLISH_DMA_RX_MATCH_OFFSET(n) (0x01d8 + 4 * (n))
/* Publish RX MATCH event n */
+#define NRF54L_SPIM_PUBLISH_DMA_TX_END_OFFSET 0x01e8
/* Publish TX END event */
+#define NRF54L_SPIM_PUBLISH_DMA_TX_READY_OFFSET 0x01ec
/* Publish TX READY event */
+#define NRF54L_SPIM_PUBLISH_DMA_TX_BUSERROR_OFFSET 0x01f0
/* Publish TX BUSERROR event */
+#define NRF54L_SPIM_SHORTS_OFFSET 0x0200
/* Event/task shortcuts */
+#define NRF54L_SPIM_INTENSET_OFFSET 0x0304
/* Enable interrupt */
+#define NRF54L_SPIM_INTENCLR_OFFSET 0x0308
/* Disable interrupt */
+#define NRF54L_SPIM_ENABLE_OFFSET 0x0500
/* Enable SPIM */
+#define NRF54L_SPIM_PRESCALER_OFFSET 0x052c
/* Core clock to SCK divisor */
+#define NRF54L_SPIM_CONFIG_OFFSET 0x0554
/* Configuration */
+#define NRF54L_SPIM_RXDELAY_OFFSET 0x05ac
/* Input sample delay */
+#define NRF54L_SPIM_CSNDUR_OFFSET 0x05b0
/* CSN to SCK duration */
+#define NRF54L_SPIM_DCXCNT_OFFSET 0x05b4
/* DCX configuration */
+#define NRF54L_SPIM_CSNPOL_OFFSET 0x05b8
/* CSN polarity */
+#define NRF54L_SPIM_ORC_OFFSET 0x05c0
/* Over-read character */
+#define NRF54L_SPIM_PSELSCK_OFFSET 0x0600
/* SCK pin select */
+#define NRF54L_SPIM_PSELMOSI_OFFSET 0x0604
/* MOSI pin select */
+#define NRF54L_SPIM_PSELMISO_OFFSET 0x0608
/* MISO pin select */
+#define NRF54L_SPIM_PSELDCX_OFFSET 0x060c
/* DCX pin select */
+#define NRF54L_SPIM_PSELCSN_OFFSET 0x0610
/* CSN pin select */
+#define NRF54L_SPIM_DMA_RX_PTR_OFFSET 0x0704
/* RX buffer address */
+#define NRF54L_SPIM_DMA_RX_MAXCNT_OFFSET 0x0708
/* RX buffer size */
+#define NRF54L_SPIM_DMA_RX_AMOUNT_OFFSET 0x070c
/* Number of received bytes */
+#define NRF54L_SPIM_DMA_RX_LIST_OFFSET 0x0714
/* RX list type */
+#define NRF54L_SPIM_DMA_RX_TERMINATEONBUSERROR_OFFSET 0x071c
/* Terminate RX DMA on bus error */
+#define NRF54L_SPIM_DMA_RX_BUSERRORADDRESS_OFFSET 0x0720
/* Address of last RX DMA bus error */
+#define NRF54L_SPIM_DMA_RX_MATCH_CONFIG_OFFSET 0x0724
/* RX DMA match configuration */
+#define NRF54L_SPIM_DMA_RX_MATCH_CANDIDATE_OFFSET(n) (0x0728 + 4 * (n))
/* RX DMA match candidate n */
+#define NRF54L_SPIM_DMA_TX_PTR_OFFSET 0x073c
/* TX buffer address */
+#define NRF54L_SPIM_DMA_TX_MAXCNT_OFFSET 0x0740
/* TX buffer size */
+#define NRF54L_SPIM_DMA_TX_AMOUNT_OFFSET 0x0744
/* Number of transmitted bytes */
+#define NRF54L_SPIM_DMA_TX_LIST_OFFSET 0x074c
/* TX list type */
+#define NRF54L_SPIM_DMA_TX_TERMINATEONBUSERROR_OFFSET 0x0754
/* Terminate TX DMA on bus error */
+#define NRF54L_SPIM_DMA_TX_BUSERRORADDRESS_OFFSET 0x0758
/* Address of last TX DMA bus error */
+
+/* Register Bitfield Definitions for SPIM ***********************************/
+
+/* Task and event registers */
+
+#define SPIM_TASKS_START (1 << 0)
+#define SPIM_TASKS_STOP (1 << 0)
+#define SPIM_TASKS_SUSPEND (1 << 0)
+#define SPIM_TASKS_RESUME (1 << 0)
+#define SPIM_EVENTS_STARTED (1 << 0)
+#define SPIM_EVENTS_STOPPED (1 << 0)
+#define SPIM_EVENTS_END (1 << 0)
+
+/* SHORTS register */
+
+#define SPIM_SHORTS_ENDSTART (1 << 17)
+#define SPIM_SHORTS_MATCH_ENABLE(n) (1 << (21 + (n)))
+#define SPIM_SHORTS_MATCH_DISABLE(n) (1 << (25 + (n)))
+
+/* INTENSET/INTENCLR registers */
+
+#define SPIM_INT_STARTED (1 << 0)
+#define SPIM_INT_STOPPED (1 << 1)
+#define SPIM_INT_END (1 << 2)
+#define SPIM_INT_DMA_RX_END (1 << 19)
+#define SPIM_INT_DMA_RX_READY (1 << 20)
+#define SPIM_INT_DMA_RX_BUSERROR (1 << 21)
+#define SPIM_INT_DMA_RX_MATCH(n) (1 << (22 + (n)))
+#define SPIM_INT_DMA_TX_END (1 << 26)
+#define SPIM_INT_DMA_TX_READY (1 << 27)
+#define SPIM_INT_DMA_TX_BUSERROR (1 << 28)
+
+/* ENABLE register */
+
+#define SPIM_ENABLE_DIS (0)
+#define SPIM_ENABLE_EN (7)
+
+/* PSEL registers */
+
+#define SPIM_PSEL_PIN_SHIFT (0)
+#define SPIM_PSEL_PIN_MASK (0x1f << SPIM_PSEL_PIN_SHIFT)
+#define SPIM_PSEL_PORT_SHIFT (5)
+#define SPIM_PSEL_PORT_MASK (0x7 << SPIM_PSEL_PORT_SHIFT)
+#define SPIM_PSEL_CONNECTED (0 << 31)
+#define SPIM_PSEL_DISCONNECTED (1 << 31)
+#define SPIM_PSEL_RESET (0xffffffff)
+
+/* PRESCALER register: even divisors only */
+
+#define SPIM_PRESCALER_DIVISOR_SHIFT (0)
+#define SPIM_PRESCALER_DIVISOR_MASK (0x7f <<
SPIM_PRESCALER_DIVISOR_SHIFT)
+#define SPIM_PRESCALER_DIVISOR_MIN (2)
+#define SPIM_PRESCALER_DIVISOR_MAX (126)
+
+/* CONFIG register */
+
+#define SPIM_CONFIG_ORDER (1 << 0)
+#define SPIM_CONFIG_CPHA (1 << 1)
+#define SPIM_CONFIG_CPOL (1 << 2)
+
+/* DMA registers */
+
+#define SPIM_DMA_MAXCNT_SHIFT (0)
+#define SPIM_DMA_MAXCNT_MASK (0xffff << SPIM_DMA_MAXCNT_SHIFT)
+#define SPIM_DMA_AMOUNT_SHIFT (0)
+#define SPIM_DMA_AMOUNT_MASK (0xffff << SPIM_DMA_AMOUNT_SHIFT)
+#define SPIM_DMA_LIST_DISABLED (0)
+#define SPIM_DMA_LIST_ARRAYLIST (1)
+#define SPIM_DMA_TERMINATEONBUSERROR (1)
+#define SPIM_DMA_MATCH_ENABLE(n) (1 << (n))
+#define SPIM_DMA_MATCH_ONESHOT(n) (1 << (16 + (n)))
+#define SPIM_DMA_MATCH_CANDIDATE_MASK (0xff)
+
+/* DPPI subscribe and publish registers */
+
+#define SPIM_DPPI_CHIDX_SHIFT (0)
+#define SPIM_DPPI_CHIDX_MASK (0xff << SPIM_DPPI_CHIDX_SHIFT)
+#define SPIM_DPPI_EN (1 << 31)
+
+/* IFTIMING, DCXCNT, CSNPOL and ORC registers */
+
+#define SPIM_RXDELAY_SHIFT (0)
+#define SPIM_RXDELAY_MASK (0x7 << SPIM_RXDELAY_SHIFT)
+#define SPIM_CSNDUR_SHIFT (0)
+#define SPIM_CSNDUR_MASK (0xff << SPIM_CSNDUR_SHIFT)
+#define SPIM_DCXCNT_SHIFT (0)
+#define SPIM_DCXCNT_MASK (0xf << SPIM_DCXCNT_SHIFT)
+#define SPIM_CSNPOL_LOW (0)
+#define SPIM_CSNPOL_HIGH (1)
+#define SPIM_ORC_SHIFT (0)
+#define SPIM_ORC_MASK (0xff << SPIM_ORC_SHIFT)
+
+#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_SPI_H */
diff --git a/arch/arm/src/nrf54l/nrf54l_spi.c b/arch/arm/src/nrf54l/nrf54l_spi.c
new file mode 100644
index 00000000000..5920d1b6f5b
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_spi.c
@@ -0,0 +1,1640 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_spi.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <nuttx/debug.h>
+#include <inttypes.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/mutex.h>
+#include <arch/barriers.h>
+#include <arch/board/board.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+
+#include "nrf54l_gpio.h"
+#include "nrf54l_spi.h"
+
+#include "hardware/nrf54l_spi.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nrf54l_spidev_s
+{
+ struct spi_dev_s spidev; /* Externally visible part of the SPI interface
*/
+ uint32_t base; /* Base address of SPI register */
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ uint32_t irq; /* SPI IRQ number */
+#endif
+ nrf54l_pinset_t sck_pin; /* SCK pin configuration */
+ uint32_t frequency; /* Requested clock frequency */
+ uint8_t mode; /* Mode 0,1,2,3 */
+
+ mutex_t lock; /* Held while chip is selected for mutual
+ * exclusion
+ */
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ sem_t sem_isr; /* Interrupt wait semaphore */
+#endif
+ bool initialized;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static inline void nrf54l_spi_putreg(struct nrf54l_spidev_s *priv,
+ uint32_t offset,
+ uint32_t value);
+static inline uint32_t nrf54l_spi_getreg(struct nrf54l_spidev_s *priv,
+ uint32_t offset);
+
+/* SPI methods */
+
+static int nrf54l_spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t nrf54l_spi_setfrequency(struct spi_dev_s *dev,
+ uint32_t frequency);
+static void nrf54l_spi_setmode(struct spi_dev_s *priv,
+ enum spi_mode_e mode);
+static void nrf54l_spi_setbits(struct spi_dev_s *priv, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int nrf54l_spi_hwfeatures(struct spi_dev_s *dev,
+ spi_hwfeatures_t features);
+#endif
+static uint32_t nrf54l_spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void nrf54l_spi_exchange(struct spi_dev_s *dev,
+ const void *txbuffer,
+ void *rxbuffer, size_t nwords);
+#ifndef CONFIG_SPI_EXCHANGE
+static void nrf54l_spi_sndblock(struct spi_dev_s *dev,
+ const void *txbuffer,
+ size_t nwords);
+static void nrf54l_spi_recvblock(struct spi_dev_s *dev,
+ void *rxbuffer,
+ size_t nwords);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+static int nrf54l_spi_isr(int irq, void *context, void *arg);
+#endif
+
+/* Initialization */
+
+static int nrf54l_spi_init(struct nrf54l_spidev_s *priv);
+static void nrf54l_spi_pselinit(struct nrf54l_spidev_s *priv,
+ uint32_t offset, nrf54l_pinset_t pinset);
+static void nrf54l_spi_gpioinit(struct nrf54l_spidev_s *priv);
+
+#ifdef CONFIG_PM
+static int nrf54l_spi_deinit(struct nrf54l_spidev_s *priv);
+static void nrf54l_spi_gpiodeinit(struct nrf54l_spidev_s *priv);
+
+static int nrf54l_spi_pm_prepare(struct pm_callback_s *cb, int domain,
+ enum pm_state_e pmstate);
+static void nrf54l_spi_pm_notify(struct pm_callback_s *cb, int domain,
+ enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+struct pm_callback_s g_pm_callbacks =
+{
+ .prepare = nrf54l_spi_pm_prepare,
+ .notify = nrf54l_spi_pm_notify
+};
+#endif
+
+/* SPI0 */
+
+#ifdef CONFIG_NRF54L_SPI0_MASTER
+static const struct spi_ops_s g_spi0ops =
+{
+ .lock = nrf54l_spi_lock,
+ .select = nrf54l_spi0select,
+ .setfrequency = nrf54l_spi_setfrequency,
+ .setmode = nrf54l_spi_setmode,
+ .setbits = nrf54l_spi_setbits,
+# ifdef CONFIG_SPI_HWFEATURES
+ .hwfeatures = nrf54l_spi_hwfeatures,
+# endif
+ .status = nrf54l_spi0status,
+# ifdef CONFIG_SPI_CMDDATA
+ .cmddata = nrf54l_spi0cmddata,
+# endif
+ .send = nrf54l_spi_send,
+# ifdef CONFIG_SPI_EXCHANGE
+ .exchange = nrf54l_spi_exchange,
+# else
+ .sndblock = nrf54l_spi_sndblock,
+ .recvblock = nrf54l_spi_recvblock,
+# endif
+#ifdef CONFIG_SPI_TRIGGER
+ .trigger = nrf54l_spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+ .registercallback = nrf54l_spi0register, /* Provided externally */
+#else
+ .registercallback = NULL, /* Not implemented */
+#endif
+};
+
+static struct nrf54l_spidev_s g_spi0dev =
+{
+ .spidev =
+ {
+ .ops = &g_spi0ops,
+ },
+
+ .base = NRF54L_SPIM20_BASE,
+ .lock = NXMUTEX_INITIALIZER,
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL20,
+#endif
+ .sck_pin = BOARD_SPI0_SCK_PIN,
+ .frequency = 0,
+ .mode = 0
+};
+#endif
+
+/* SPI1 */
+
+#ifdef CONFIG_NRF54L_SPI1_MASTER
+static const struct spi_ops_s g_spi1ops =
+{
+ .lock = nrf54l_spi_lock,
+ .select = nrf54l_spi1select,
+ .setfrequency = nrf54l_spi_setfrequency,
+ .setmode = nrf54l_spi_setmode,
+ .setbits = nrf54l_spi_setbits,
+# ifdef CONFIG_SPI_HWFEATURES
+ .hwfeatures = nrf54l_spi_hwfeatures,
+# endif
+ .status = nrf54l_spi1status,
+# ifdef CONFIG_SPI_CMDDATA
+ .cmddata = nrf54l_spi1cmddata,
+# endif
+ .send = nrf54l_spi_send,
+# ifdef CONFIG_SPI_EXCHANGE
+ .exchange = nrf54l_spi_exchange,
+# else
+ .sndblock = nrf54l_spi_sndblock,
+ .recvblock = nrf54l_spi_recvblock,
+# endif
+#ifdef CONFIG_SPI_TRIGGER
+ .trigger = nrf54l_spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+ .registercallback = nrf54l_spi1register, /* Provided externally */
+#else
+ .registercallback = NULL, /* Not implemented */
+#endif
+};
+
+static struct nrf54l_spidev_s g_spi1dev =
+{
+ .spidev =
+ {
+ .ops = &g_spi1ops,
+ },
+
+ .base = NRF54L_SPIM21_BASE,
+ .lock = NXMUTEX_INITIALIZER,
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL21,
+#endif
+ .sck_pin = BOARD_SPI1_SCK_PIN,
+ .frequency = 0,
+ .mode = 0
+};
+#endif
+
+/* SPI2 */
+
+#ifdef CONFIG_NRF54L_SPI2_MASTER
+static const struct spi_ops_s g_spi2ops =
+{
+ .lock = nrf54l_spi_lock,
+ .select = nrf54l_spi2select,
+ .setfrequency = nrf54l_spi_setfrequency,
+ .setmode = nrf54l_spi_setmode,
+ .setbits = nrf54l_spi_setbits,
+# ifdef CONFIG_SPI_HWFEATURES
+ .hwfeatures = nrf54l_spi_hwfeatures,
+# endif
+ .status = nrf54l_spi2status,
+# ifdef CONFIG_SPI_CMDDATA
+ .cmddata = nrf54l_spi2cmddata,
+# endif
+ .send = nrf54l_spi_send,
+# ifdef CONFIG_SPI_EXCHANGE
+ .exchange = nrf54l_spi_exchange,
+# else
+ .sndblock = nrf54l_spi_sndblock,
+ .recvblock = nrf54l_spi_recvblock,
+# endif
+#ifdef CONFIG_SPI_TRIGGER
+ .trigger = nrf54l_spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+ .registercallback = nrf54l_spi2register, /* Provided externally */
+#else
+ .registercallback = NULL, /* Not implemented */
+#endif
+};
+
+static struct nrf54l_spidev_s g_spi2dev =
+{
+ .spidev =
+ {
+ .ops = &g_spi2ops,
+ },
+
+ .base = NRF54L_SPIM22_BASE,
+ .lock = NXMUTEX_INITIALIZER,
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL22,
+#endif
+ .sck_pin = BOARD_SPI2_SCK_PIN,
+ .frequency = 0,
+ .mode = 0
+};
+#endif
+
+/* SPI3 */
+
+#ifdef CONFIG_NRF54L_SPI3_MASTER
+static const struct spi_ops_s g_spi3ops =
+{
+ .lock = nrf54l_spi_lock,
+ .select = nrf54l_spi3select,
+ .setfrequency = nrf54l_spi_setfrequency,
+ .setmode = nrf54l_spi_setmode,
+ .setbits = nrf54l_spi_setbits,
+# ifdef CONFIG_SPI_HWFEATURES
+ .hwfeatures = nrf54l_spi_hwfeatures,
+# endif
+ .status = nrf54l_spi3status,
+# ifdef CONFIG_SPI_CMDDATA
+ .cmddata = nrf54l_spi3cmddata,
+# endif
+ .send = nrf54l_spi_send,
+# ifdef CONFIG_SPI_EXCHANGE
+ .exchange = nrf54l_spi_exchange,
+# else
+ .sndblock = nrf54l_spi_sndblock,
+ .recvblock = nrf54l_spi_recvblock,
+# endif
+#ifdef CONFIG_SPI_TRIGGER
+ .trigger = nrf54l_spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+ .registercallback = nrf54l_spi3register, /* Provided externally */
+#else
+ .registercallback = NULL, /* Not implemented */
+#endif
+};
+
+static struct nrf54l_spidev_s g_spi3dev =
+{
+ .spidev =
+ {
+ .ops = &g_spi3ops,
+ },
+
+ .base = NRF54L_SPIM30_BASE,
+ .lock = NXMUTEX_INITIALIZER,
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL30,
+#endif
+ .sck_pin = BOARD_SPI3_SCK_PIN,
+ .frequency = 0,
+ .mode = 0
+};
+#endif
+
+/* SPI4 */
+
+#ifdef CONFIG_NRF54L_SPI4_MASTER
+static const struct spi_ops_s g_spi4ops =
+{
+ .lock = nrf54l_spi_lock,
+ .select = nrf54l_spi4select,
+ .setfrequency = nrf54l_spi_setfrequency,
+ .setmode = nrf54l_spi_setmode,
+ .setbits = nrf54l_spi_setbits,
+# ifdef CONFIG_SPI_HWFEATURES
+ .hwfeatures = nrf54l_spi_hwfeatures,
+# endif
+ .status = nrf54l_spi4status,
+# ifdef CONFIG_SPI_CMDDATA
+ .cmddata = nrf54l_spi4cmddata,
+# endif
+ .send = nrf54l_spi_send,
+# ifdef CONFIG_SPI_EXCHANGE
+ .exchange = nrf54l_spi_exchange,
+# else
+ .sndblock = nrf54l_spi_sndblock,
+ .recvblock = nrf54l_spi_recvblock,
+# endif
+#ifdef CONFIG_SPI_TRIGGER
+ .trigger = nrf54l_spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+ .registercallback = nrf54l_spi4register, /* Provided externally */
+#else
+ .registercallback = NULL, /* Not implemented */
+#endif
+};
+
+static struct nrf54l_spidev_s g_spi4dev =
+{
+ .spidev =
+ {
+ .ops = &g_spi4ops,
+ },
+
+ .base = NRF54L_SPIM00_BASE,
+ .lock = NXMUTEX_INITIALIZER,
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL00,
+#endif
+ .sck_pin = BOARD_SPI4_SCK_PIN,
+ .frequency = 0,
+ .mode = 0
+};
+#endif
+
+/* SPI5 */
+
+#ifdef CONFIG_NRF54L_SPI5_MASTER
+static const struct spi_ops_s g_spi5ops =
+{
+ .lock = nrf54l_spi_lock,
+ .select = nrf54l_spi5select,
+ .setfrequency = nrf54l_spi_setfrequency,
+ .setmode = nrf54l_spi_setmode,
+ .setbits = nrf54l_spi_setbits,
+# ifdef CONFIG_SPI_HWFEATURES
+ .hwfeatures = nrf54l_spi_hwfeatures,
+# endif
+ .status = nrf54l_spi5status,
+# ifdef CONFIG_SPI_CMDDATA
+ .cmddata = nrf54l_spi5cmddata,
+# endif
+ .send = nrf54l_spi_send,
+# ifdef CONFIG_SPI_EXCHANGE
+ .exchange = nrf54l_spi_exchange,
+# else
+ .sndblock = nrf54l_spi_sndblock,
+ .recvblock = nrf54l_spi_recvblock,
+# endif
+#ifdef CONFIG_SPI_TRIGGER
+ .trigger = nrf54l_spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+ .registercallback = nrf54l_spi5register, /* Provided externally */
+#else
+ .registercallback = NULL, /* Not implemented */
+#endif
+};
+
+static struct nrf54l_spidev_s g_spi5dev =
+{
+ .spidev =
+ {
+ .ops = &g_spi5ops,
+ },
+
+ .base = NRF54L_SPIM23_BASE,
+ .lock = NXMUTEX_INITIALIZER,
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL23,
+#endif
+ .sck_pin = BOARD_SPI5_SCK_PIN,
+ .frequency = 0,
+ .mode = 0
+};
+#endif
+
+/* SPI6 */
+
+#ifdef CONFIG_NRF54L_SPI6_MASTER
+static const struct spi_ops_s g_spi6ops =
+{
+ .lock = nrf54l_spi_lock,
+ .select = nrf54l_spi6select,
+ .setfrequency = nrf54l_spi_setfrequency,
+ .setmode = nrf54l_spi_setmode,
+ .setbits = nrf54l_spi_setbits,
+# ifdef CONFIG_SPI_HWFEATURES
+ .hwfeatures = nrf54l_spi_hwfeatures,
+# endif
+ .status = nrf54l_spi6status,
+# ifdef CONFIG_SPI_CMDDATA
+ .cmddata = nrf54l_spi6cmddata,
+# endif
+ .send = nrf54l_spi_send,
+# ifdef CONFIG_SPI_EXCHANGE
+ .exchange = nrf54l_spi_exchange,
+# else
+ .sndblock = nrf54l_spi_sndblock,
+ .recvblock = nrf54l_spi_recvblock,
+# endif
+#ifdef CONFIG_SPI_TRIGGER
+ .trigger = nrf54l_spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+ .registercallback = nrf54l_spi6register, /* Provided externally */
+#else
+ .registercallback = NULL, /* Not implemented */
+#endif
+};
+
+static struct nrf54l_spidev_s g_spi6dev =
+{
+ .spidev =
+ {
+ .ops = &g_spi6ops,
+ },
+
+ .base = NRF54L_SPIM24_BASE,
+ .lock = NXMUTEX_INITIALIZER,
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ .sem_isr = SEM_INITIALIZER(0),
+ .irq = NRF54L_IRQ_SERIAL24,
+#endif
+ .sck_pin = BOARD_SPI6_SCK_PIN,
+ .frequency = 0,
+ .mode = 0
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_spi_putreg
+ *
+ * Description:
+ * Put a 32-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void nrf54l_spi_putreg(struct nrf54l_spidev_s *priv,
+ uint32_t offset,
+ uint32_t value)
+{
+ putreg32(value, priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf54l_spi_getreg
+ *
+ * Description:
+ * Get a 32-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint32_t nrf54l_spi_getreg(struct nrf54l_spidev_s *priv,
+ uint32_t offset)
+{
+ return getreg32(priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf54l_spi_isr
+ *
+ * Description:
+ * Common SPI interrupt service routine
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+static int nrf54l_spi_isr(int irq, void *context, void *arg)
+{
+ struct nrf54l_spidev_s *priv = (struct nrf54l_spidev_s *)arg;
+
+ /* Get interrupt event */
+
+ if (nrf54l_spi_getreg(priv, NRF54L_SPIM_EVENTS_END_OFFSET) == 1)
+ {
+ /* Transfer is complete */
+
+ nxsem_post(&priv->sem_isr);
+
+ /* Clear event */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_EVENTS_END_OFFSET, 0);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nrf54l_spi_init
+ *
+ * Description:
+ * Configure SPI
+ *
+ ****************************************************************************/
+
+static int nrf54l_spi_init(struct nrf54l_spidev_s *priv)
+{
+ /* Disable SPI */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_ENABLE_OFFSET, SPIM_ENABLE_DIS);
+
+ /* Configure SPI pins */
+
+ nrf54l_spi_gpioinit(priv);
+
+ /* NOTE: Chip select pin must be configured by board-specific logic */
+
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ /* Enable interrupts for RX and TX done */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_INTENSET_OFFSET, SPIM_INT_END);
+#endif
+
+ /* Enable SPI */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_ENABLE_OFFSET, SPIM_ENABLE_EN);
+
+ return OK;
+}
+
+#ifdef CONFIG_PM
+/****************************************************************************
+ * Name: nrf54l_spi_deinit
+ *
+ * Description:
+ * Configure SPI
+ *
+ ****************************************************************************/
+
+static int nrf54l_spi_deinit(struct nrf54l_spidev_s *priv)
+{
+ /* Disable SPI */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_ENABLE_OFFSET, SPIM_ENABLE_DIS);
+
+ /* Unconfigure SPI pins */
+
+ nrf54l_spi_gpiodeinit(priv);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: nrf54l_spi_pselinit
+ *
+ * Description:
+ * Configure PSEL for SPI devices
+ *
+ ****************************************************************************/
+
+static void nrf54l_spi_pselinit(struct nrf54l_spidev_s *priv,
+ uint32_t offset, nrf54l_pinset_t pinset)
+{
+ uint32_t regval;
+ int pin = GPIO_PIN_DECODE(pinset);
+ int port = GPIO_PORT_DECODE(pinset);
+
+ regval = (pin << SPIM_PSEL_PIN_SHIFT);
+ regval |= (port << SPIM_PSEL_PORT_SHIFT);
+ nrf54l_spi_putreg(priv, offset, regval);
+}
+
+/****************************************************************************
+ * Name: nrf54l_spi_gpioinit
+ *
+ * Description:
+ * Configure GPIO for SPI pins
+ *
+ ****************************************************************************/
+
+static void nrf54l_spi_gpioinit(struct nrf54l_spidev_s *priv)
+{
+ nrf54l_gpio_config(priv->sck_pin);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELSCK_OFFSET, priv->sck_pin);
+
+#ifdef CONFIG_NRF54L_SPI0_MASTER
+ if (priv == &g_spi0dev)
+ {
+#ifdef BOARD_SPI0_MISO_PIN
+ nrf54l_gpio_config(BOARD_SPI0_MISO_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMISO_OFFSET,
+ BOARD_SPI0_MISO_PIN);
+#endif
+#ifdef BOARD_SPI0_MOSI_PIN
+ nrf54l_gpio_config(BOARD_SPI0_MOSI_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMOSI_OFFSET,
+ BOARD_SPI0_MOSI_PIN);
+ nrf54l_gpio_write(BOARD_SPI0_MOSI_PIN, false);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI1_MASTER
+ if (priv == &g_spi1dev)
+ {
+#ifdef BOARD_SPI1_MISO_PIN
+ nrf54l_gpio_config(BOARD_SPI1_MISO_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMISO_OFFSET,
+ BOARD_SPI1_MISO_PIN);
+#endif
+#ifdef BOARD_SPI1_MOSI_PIN
+ nrf54l_gpio_config(BOARD_SPI1_MOSI_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMOSI_OFFSET,
+ BOARD_SPI1_MOSI_PIN);
+ nrf54l_gpio_write(BOARD_SPI1_MOSI_PIN, false);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI2_MASTER
+ if (priv == &g_spi2dev)
+ {
+#ifdef BOARD_SPI2_MISO_PIN
+ nrf54l_gpio_config(BOARD_SPI2_MISO_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMISO_OFFSET,
+ BOARD_SPI2_MISO_PIN);
+#endif
+#ifdef BOARD_SPI2_MOSI_PIN
+ nrf54l_gpio_config(BOARD_SPI2_MOSI_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMOSI_OFFSET,
+ BOARD_SPI2_MOSI_PIN);
+ nrf54l_gpio_write(BOARD_SPI2_MOSI_PIN, false);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI3_MASTER
+ if (priv == &g_spi3dev)
+ {
+#ifdef BOARD_SPI3_MISO_PIN
+ nrf54l_gpio_config(BOARD_SPI3_MISO_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMISO_OFFSET,
+ BOARD_SPI3_MISO_PIN);
+#endif
+#ifdef BOARD_SPI3_MOSI_PIN
+ nrf54l_gpio_config(BOARD_SPI3_MOSI_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMOSI_OFFSET,
+ BOARD_SPI3_MOSI_PIN);
+ nrf54l_gpio_write(BOARD_SPI3_MOSI_PIN, false);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI4_MASTER
+ if (priv == &g_spi4dev)
+ {
+#ifdef BOARD_SPI4_MISO_PIN
+ nrf54l_gpio_config(BOARD_SPI4_MISO_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMISO_OFFSET,
+ BOARD_SPI4_MISO_PIN);
+#endif
+#ifdef BOARD_SPI4_MOSI_PIN
+ nrf54l_gpio_config(BOARD_SPI4_MOSI_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMOSI_OFFSET,
+ BOARD_SPI4_MOSI_PIN);
+ nrf54l_gpio_write(BOARD_SPI4_MOSI_PIN, false);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI5_MASTER
+ if (priv == &g_spi5dev)
+ {
+#ifdef BOARD_SPI5_MISO_PIN
+ nrf54l_gpio_config(BOARD_SPI5_MISO_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMISO_OFFSET,
+ BOARD_SPI5_MISO_PIN);
+#endif
+#ifdef BOARD_SPI5_MOSI_PIN
+ nrf54l_gpio_config(BOARD_SPI5_MOSI_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMOSI_OFFSET,
+ BOARD_SPI5_MOSI_PIN);
+ nrf54l_gpio_write(BOARD_SPI5_MOSI_PIN, false);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI6_MASTER
+ if (priv == &g_spi6dev)
+ {
+#ifdef BOARD_SPI6_MISO_PIN
+ nrf54l_gpio_config(BOARD_SPI6_MISO_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMISO_OFFSET,
+ BOARD_SPI6_MISO_PIN);
+#endif
+#ifdef BOARD_SPI6_MOSI_PIN
+ nrf54l_gpio_config(BOARD_SPI6_MOSI_PIN);
+ nrf54l_spi_pselinit(priv, NRF54L_SPIM_PSELMOSI_OFFSET,
+ BOARD_SPI6_MOSI_PIN);
+ nrf54l_gpio_write(BOARD_SPI6_MOSI_PIN, false);
+#endif
+ }
+#endif
+}
+
+#ifdef CONFIG_PM
+/****************************************************************************
+ * Name: nrf54l_spi_gpioinit
+ *
+ * Description:
+ * Configure GPIO for SPI pins
+ *
+ ****************************************************************************/
+
+static void nrf54l_spi_gpiodeinit(struct nrf54l_spidev_s *priv)
+{
+ nrf54l_gpio_unconfig(priv->sck_pin);
+
+#ifdef CONFIG_NRF54L_SPI0_MASTER
+ if (priv == &g_spi0dev)
+ {
+#ifdef BOARD_SPI0_MISO_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI0_MISO_PIN);
+#endif
+#ifdef BOARD_SPI0_MOSI_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI0_MOSI_PIN);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI1_MASTER
+ if (priv == &g_spi1dev)
+ {
+#ifdef BOARD_SPI1_MISO_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI1_MISO_PIN);
+#endif
+#ifdef BOARD_SPI1_MOSI_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI1_MOSI_PIN);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI2_MASTER
+ if (priv == &g_spi2dev)
+ {
+#ifdef BOARD_SPI2_MISO_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI2_MISO_PIN);
+#endif
+#ifdef BOARD_SPI2_MOSI_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI2_MOSI_PIN);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI3_MASTER
+ if (priv == &g_spi3dev)
+ {
+#ifdef BOARD_SPI3_MISO_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI3_MISO_PIN);
+#endif
+#ifdef BOARD_SPI3_MOSI_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI3_MOSI_PIN);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI4_MASTER
+ if (priv == &g_spi4dev)
+ {
+#ifdef BOARD_SPI4_MISO_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI4_MISO_PIN);
+#endif
+#ifdef BOARD_SPI4_MOSI_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI4_MOSI_PIN);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI5_MASTER
+ if (priv == &g_spi5dev)
+ {
+#ifdef BOARD_SPI5_MISO_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI5_MISO_PIN);
+#endif
+#ifdef BOARD_SPI5_MOSI_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI5_MOSI_PIN);
+#endif
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI6_MASTER
+ if (priv == &g_spi6dev)
+ {
+#ifdef BOARD_SPI6_MISO_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI6_MISO_PIN);
+#endif
+#ifdef BOARD_SPI6_MOSI_PIN
+ nrf54l_gpio_unconfig(BOARD_SPI6_MOSI_PIN);
+#endif
+ }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: nrf54l_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 nrf54l_spi_lock(struct spi_dev_s *dev, bool lock)
+{
+ struct nrf54l_spidev_s *priv = (struct nrf54l_spidev_s *)dev;
+ int ret = OK;
+
+ if (lock)
+ {
+ ret = nxmutex_lock(&priv->lock);
+ }
+ else
+ {
+ ret = nxmutex_unlock(&priv->lock);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_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 nrf54l_spi_setfrequency(struct spi_dev_s *dev,
+ uint32_t frequency)
+{
+ struct nrf54l_spidev_s *priv = (struct nrf54l_spidev_s *)dev;
+ uint32_t regval = 0;
+ uint32_t clock;
+
+ if (priv->frequency == frequency)
+ {
+ /* We are already at this frequency */
+
+ return priv->frequency;
+ }
+
+ /* SCK is the peripheral clock divided by an even prescaler.
+ * SPIM00 runs from the 128MHz core clock, the others from 16MHz.
+ */
+
+ clock = (priv->base == NRF54L_SPIM00_BASE) ? 128000000 : 16000000;
+
+ regval = (clock + frequency - 1) / frequency;
+ regval = (regval + 1) & ~1;
+
+ if (regval < SPIM_PRESCALER_DIVISOR_MIN)
+ {
+ regval = SPIM_PRESCALER_DIVISOR_MIN;
+ }
+ else if (regval > SPIM_PRESCALER_DIVISOR_MAX)
+ {
+ regval = SPIM_PRESCALER_DIVISOR_MAX;
+ }
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_PRESCALER_OFFSET, regval);
+
+ priv->frequency = clock / regval;
+
+ spiinfo("Frequency %" PRId32 "->%" PRId32 "\n", frequency,
+ priv->frequency);
+
+ return priv->frequency;
+}
+
+/****************************************************************************
+ * Name: nrf54l_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 nrf54l_spi_setmode(struct spi_dev_s *dev,
+ enum spi_mode_e mode)
+{
+ struct nrf54l_spidev_s *priv = (struct nrf54l_spidev_s *)dev;
+ uint32_t regval = 0;
+
+ spiinfo("mode=%d\n", mode);
+
+ /* Has the mode changed? */
+
+ if (mode != priv->mode)
+ {
+ regval = nrf54l_spi_getreg(priv, NRF54L_SPIM_CONFIG_OFFSET);
+ regval &= ~(SPIM_CONFIG_CPHA | SPIM_CONFIG_CPOL);
+
+ switch (mode)
+ {
+ case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+ {
+ break;
+ }
+
+ case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+ {
+ regval |= SPIM_CONFIG_CPHA;
+ break;
+ }
+
+ case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+ {
+ regval |= SPIM_CONFIG_CPOL;
+ break;
+ }
+
+ case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+ {
+ regval |= SPIM_CONFIG_CPHA;
+ regval |= SPIM_CONFIG_CPOL;
+ break;
+ }
+
+ default:
+ {
+ DEBUGPANIC();
+ return;
+ }
+ }
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_CONFIG_OFFSET, regval);
+
+ /* According to manual we have to set SCK pin output
+ * value the same as CPOL value
+ */
+
+ if (mode == SPIDEV_MODE2 || mode == SPIDEV_MODE3)
+ {
+ nrf54l_gpio_write(priv->sck_pin, true);
+ }
+ else
+ {
+ nrf54l_gpio_write(priv->sck_pin, false);
+ }
+
+ priv->mode = mode;
+ }
+}
+
+/****************************************************************************
+ * Name: nrf54l_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 nrf54l_spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+ if (nbits != 8)
+ {
+ spierr("ERROR: nbits not supported: %d\n", nbits);
+ }
+}
+
+/****************************************************************************
+ * Name: nrf54l_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 nrf54l_spi_hwfeatures(struct spi_dev_s *dev,
+ spi_hwfeatures_t features)
+{
+#ifdef CONFIG_SPI_BITORDER
+ struct nrf54l_spidev_s *priv = (struct nrf54l_spidev_s *)dev;
+ uint32_t setbits = 0;
+ uint32_t clrbits = 0;
+ uint32_t regval;
+
+ spiinfo("features=%08x\n", features);
+
+ /* Transfer data LSB first? */
+
+ if ((features & HWFEAT_LSBFIRST) != 0)
+ {
+ setbits = SPIM_CONFIG_ORDER;
+ clrbits = 0;
+ }
+ else
+ {
+ setbits = 0;
+ clrbits = SPIM_CONFIG_ORDER;
+ }
+
+ regval = nrf54l_spi_getreg(priv, NRF54L_SPIM_CONFIG_OFFSET);
+ regval &= ~clrbits;
+ regval |= setbits;
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_CONFIG_OFFSET, regval);
+
+#endif
+ /* Other H/W features are not supported */
+
+ return ((features & ~HWFEAT_LSBFIRST) == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: n4f52_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 nrf54l_spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+ uint32_t ret = 0;
+
+ /* Exchange one word on SPI */
+
+ nrf54l_spi_exchange(dev, &wd, &ret, 1);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_spi_exchange
+ *
+ * Description:
+ * Exchange a block of data on SPI without 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.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void nrf54l_spi_exchange(struct spi_dev_s *dev,
+ const void *txbuffer,
+ void *rxbuffer, size_t nwords)
+{
+ struct nrf54l_spidev_s *priv = (struct nrf54l_spidev_s *)dev;
+ uint32_t regval = 0;
+ size_t nwords_left = nwords;
+
+ if (rxbuffer != NULL)
+ {
+ /* Write RXD data pointer */
+
+ regval = (uintptr_t)rxbuffer;
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_DMA_RX_PTR_OFFSET, regval);
+ }
+ else
+ {
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_DMA_RX_MAXCNT_OFFSET, 0);
+ }
+
+ if (txbuffer != NULL)
+ {
+ /* Write TXD data pointer */
+
+ regval = (uintptr_t)txbuffer;
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_DMA_TX_PTR_OFFSET, regval);
+ }
+ else
+ {
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_DMA_TX_MAXCNT_OFFSET, 0);
+ }
+
+ /* Transfer in batches if more than MAXCNT bytes */
+
+ while (nwords_left > 0)
+ {
+ size_t transfer_size = (nwords_left > SPIM_DMA_MAXCNT_MASK ?
+ SPIM_DMA_MAXCNT_MASK : nwords_left);
+
+ if (rxbuffer != NULL)
+ {
+ /* Write number of bytes in RXD buffer */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_DMA_RX_MAXCNT_OFFSET,
+ transfer_size);
+ }
+
+ if (txbuffer != NULL)
+ {
+ /* Write number of bytes in TXD buffer */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_DMA_TX_MAXCNT_OFFSET,
+ transfer_size);
+ }
+
+ /* SPI start */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_TASK_START_OFFSET,
+ SPIM_TASKS_START);
+
+#ifndef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ /* Wait for RX done and TX done */
+
+ while (nrf54l_spi_getreg(priv, NRF54L_SPIM_EVENTS_END_OFFSET) != 1);
+
+ /* Clear event */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_EVENTS_END_OFFSET, 0);
+#else
+ /* Wait for transfer complete */
+
+ nxsem_wait_uninterruptible(&priv->sem_isr);
+#endif
+
+ regval = nrf54l_spi_getreg(priv, NRF54L_SPIM_DMA_TX_AMOUNT_OFFSET);
+ if (regval != transfer_size)
+ {
+ spierr("Incomplete transfer wrote %" PRId32 " expected %zu\n",
+ regval, transfer_size);
+ }
+
+ /* SPI stop */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_TASK_STOP_OFFSET, SPIM_TASKS_STOP);
+
+ /* Wait for STOP event */
+
+ while (nrf54l_spi_getreg(priv, NRF54L_SPIM_EVENTS_STOPPED_OFFSET)
+ != 1);
+
+ /* Clear event */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_EVENTS_STOPPED_OFFSET, 0);
+
+ nwords_left -= transfer_size;
+ }
+
+ /* Clear RX/TX DMA after transfer */
+
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_DMA_RX_PTR_OFFSET, 0);
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_DMA_RX_MAXCNT_OFFSET, 0);
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_DMA_TX_PTR_OFFSET, 0);
+ nrf54l_spi_putreg(priv, NRF54L_SPIM_DMA_TX_MAXCNT_OFFSET, 0);
+}
+
+#ifndef CONFIG_SPI_EXCHANGE
+
+/****************************************************************************
+ * Name: nrf54l_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.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void nrf54l_spi_sndblock(struct spi_dev_s *dev,
+ const void *txbuffer,
+ size_t nwords)
+{
+ spiinfo("txbuffer=%p nwords=%zu\n", txbuffer, nwords);
+ return nrf54l_spi_exchange(dev, txbuffer, NULL, nwords);
+}
+
+/****************************************************************************
+ * Name: nrf54l_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.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void nrf54l_spi_recvblock(struct spi_dev_s *dev,
+ void *rxbuffer,
+ size_t nwords)
+{
+ spiinfo("txbuffer=%p nwords=%zu\n", rxbuffer, nwords);
+ return nrf54l_spi_exchange(dev, NULL, rxbuffer, nwords);
+}
+#endif /* CONFIG_SPI_EXCHANGE */
+
+/****************************************************************************
+ * Name: nrf54l_spi_trigger
+ *
+ * Description:
+ * Trigger a previously configured DMA transfer.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ *
+ * Returned Value:
+ * OK - Trigger was fired
+ * -ENOSYS - Trigger not fired due to lack of DMA or low level support
+ * -EIO - Trigger not fired because not previously primed
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_TRIGGER
+static int nrf54l_spi_trigger(struct spi_dev_s *dev)
+{
+ return -ENOSYS;
+}
+#endif
+
+#ifdef CONFIG_PM
+/****************************************************************************
+ * Name: nrf54l_spi_pm_prepare
+ ****************************************************************************/
+
+static int nrf54l_spi_pm_prepare(struct pm_callback_s *cb, int domain,
+ enum pm_state_e pmstate)
+{
+ if (pmstate == PM_STANDBY || pmstate == PM_SLEEP)
+ {
+ bool active = false;
+
+#ifdef CONFIG_NRF54L_SPI0_MASTER
+ active |= nrf54l_spi_getreg(&g_spi0dev, SPIM_EVENTS_STARTED);
+#endif
+#ifdef CONFIG_NRF54L_SPI1_MASTER
+ active |= nrf54l_spi_getreg(&g_spi1dev, SPIM_EVENTS_STARTED);
+#endif
+#ifdef CONFIG_NRF54L_SPI2_MASTER
+ active |= nrf54l_spi_getreg(&g_spi2dev, SPIM_EVENTS_STARTED);
+#endif
+#ifdef CONFIG_NRF54L_SPI3_MASTER
+ active |= nrf54l_spi_getreg(&g_spi3dev, SPIM_EVENTS_STARTED);
+#endif
+#ifdef CONFIG_NRF54L_SPI4_MASTER
+ active |= nrf54l_spi_getreg(&g_spi4dev, SPIM_EVENTS_STARTED);
+#endif
+#ifdef CONFIG_NRF54L_SPI5_MASTER
+ active |= nrf54l_spi_getreg(&g_spi5dev, SPIM_EVENTS_STARTED);
+#endif
+#ifdef CONFIG_NRF54L_SPI6_MASTER
+ active |= nrf54l_spi_getreg(&g_spi6dev, SPIM_EVENTS_STARTED);
+#endif
+
+ if (active)
+ {
+ /* SPI is being used, cannot disable */
+
+ return -1;
+ }
+ else
+ {
+ /* SPI is inactive, can go to sleep */
+
+ return 0;
+ }
+ }
+ else
+ {
+ /* We can always go to any other state */
+
+ return 0;
+ }
+}
+
+/****************************************************************************
+ * Name: nrf54l_spi_pm_notify
+ ****************************************************************************/
+
+static void nrf54l_spi_pm_notify(struct pm_callback_s *cb, int domain,
+ enum pm_state_e pmstate)
+{
+ if (pmstate == PM_SLEEP || pmstate == PM_STANDBY)
+ {
+ /* Deinit SPI peripheral on each initialized device */
+
+#ifdef CONFIG_NRF54L_SPI0_MASTER
+ if (g_spi0dev.initialized)
+ {
+ nrf54l_spi_deinit(&g_spi0dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI1_MASTER
+ if (g_spi1dev.initialized)
+ {
+ nrf54l_spi_deinit(&g_spi1dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI2_MASTER
+ if (g_spi2dev.initialized)
+ {
+ nrf54l_spi_deinit(&g_spi2dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI3_MASTER
+ if (g_spi3dev.initialized)
+ {
+ nrf54l_spi_deinit(&g_spi3dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI4_MASTER
+ if (g_spi4dev.initialized)
+ {
+ nrf54l_spi_deinit(&g_spi4dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI5_MASTER
+ if (g_spi5dev.initialized)
+ {
+ nrf54l_spi_deinit(&g_spi5dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI6_MASTER
+ if (g_spi6dev.initialized)
+ {
+ nrf54l_spi_deinit(&g_spi6dev);
+ }
+#endif
+ }
+ else
+ {
+ /* Reinit SPI peripheral on each initialized device */
+
+#ifdef CONFIG_NRF54L_SPI0_MASTER
+ if (g_spi0dev.initialized)
+ {
+ nrf54l_spi_init(&g_spi0dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI1_MASTER
+ if (g_spi1dev.initialized)
+ {
+ nrf54l_spi_init(&g_spi1dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI2_MASTER
+ if (g_spi2dev.initialized)
+ {
+ nrf54l_spi_init(&g_spi2dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI3_MASTER
+ if (g_spi3dev.initialized)
+ {
+ nrf54l_spi_init(&g_spi3dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI4_MASTER
+ if (g_spi4dev.initialized)
+ {
+ nrf54l_spi_init(&g_spi4dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI5_MASTER
+ if (g_spi5dev.initialized)
+ {
+ nrf54l_spi_init(&g_spi5dev);
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI6_MASTER
+ if (g_spi6dev.initialized)
+ {
+ nrf54l_spi_init(&g_spi6dev);
+ }
+#endif
+ }
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_spibus_initialize
+ *
+ * Description:
+ * Initialize the selected SPI port.
+ *
+ * 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 *nrf54l_spibus_initialize(int port)
+{
+ struct nrf54l_spidev_s *priv = NULL;
+
+ /* Get SPI driver data */
+
+ switch (port)
+ {
+#ifdef CONFIG_NRF54L_SPI0_MASTER
+ case 0:
+ {
+ priv = &g_spi0dev;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI1_MASTER
+ case 1:
+ {
+ priv = &g_spi1dev;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI2_MASTER
+ case 2:
+ {
+ priv = &g_spi2dev;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI3_MASTER
+ case 3:
+ {
+ priv = &g_spi3dev;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI4_MASTER
+ case 4:
+ {
+ priv = &g_spi4dev;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI5_MASTER
+ case 5:
+ {
+ priv = &g_spi5dev;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_SPI6_MASTER
+ case 6:
+ {
+ priv = &g_spi6dev;
+ break;
+ }
+#endif
+
+ default:
+ {
+ goto errout;
+ }
+ }
+
+ /* Initialize the SPI */
+
+ nrf54l_spi_init(priv);
+
+ /* Mark device as initialized */
+
+ priv->initialized = true;
+
+#ifdef CONFIG_NRF54L_SPI_MASTER_INTERRUPTS
+ /* Attach SPI interrupt */
+
+ irq_attach(priv->irq, nrf54l_spi_isr, priv);
+ up_enable_irq(priv->irq);
+#endif
+
+errout:
+ return (struct spi_dev_s *)priv;
+}
diff --git a/arch/arm/src/nrf54l/nrf54l_spi.h b/arch/arm/src/nrf54l/nrf54l_spi.h
new file mode 100644
index 00000000000..4d04a96f84c
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_spi.h
@@ -0,0 +1,191 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_spi.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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_NRF54L_NRF54L_SPI_H
+#define __ARCH_ARM_SRC_NRF54L_NRF54L_SPI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/spi/spi.h>
+
+#include "chip.h"
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_spibus_initialize
+ *
+ * Description:
+ * Initialize the selected SPI port.
+ *
+ * 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 *nrf54l_spibus_initialize(int port);
+
+/****************************************************************************
+ * Name: nrf54l_spi0/1/...select and nrf54l_spi0/1/...status
+ *
+ * Description:
+ * The external select, status, and cmddata functions must be provided
+ * by board-specific logic for each enabled SPI bus.
+ * These are implementations of the select, status, and cmddata methods of
+ * the SPI interface defined by struct spi_ops_s (include/nuttx/spi/spi.h).
+ * All other methods (including nrf54l_spibus_initialize()) are provided by
+ * common NRF54L logic. To use this common SPI logic on your board:
+ *
+ * 1. Provide logic in nrf54l_boardinitialize() to configure SPI chip
+ * select pins.
+ * 2. Provide nrf54l_spi0/1/...select() and nrf54l_spi0/1/...status()
+ * 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 nrf54l_spi0/1/...cmddata() 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 nrf54l_spibus_initialize() in your low level
+ * application initialization logic.
+ * 5. The handle returned by nrf54l_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_NRF54L_SPI0_MASTER
+void nrf54l_spi0select(struct spi_dev_s *dev, uint32_t devid,
+ bool selected);
+uint8_t nrf54l_spi0status(struct spi_dev_s *dev, uint32_t devid);
+int nrf54l_spi0cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI1_MASTER
+void nrf54l_spi1select(struct spi_dev_s *dev, uint32_t devid,
+ bool selected);
+uint8_t nrf54l_spi1status(struct spi_dev_s *dev, uint32_t devid);
+int nrf54l_spi1cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI2_MASTER
+void nrf54l_spi2select(struct spi_dev_s *dev, uint32_t devid,
+ bool selected);
+uint8_t nrf54l_spi2status(struct spi_dev_s *dev, uint32_t devid);
+int nrf54l_spi2cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI3_MASTER
+void nrf54l_spi3select(struct spi_dev_s *dev, uint32_t devid,
+ bool selected);
+uint8_t nrf54l_spi3status(struct spi_dev_s *dev, uint32_t devid);
+int nrf54l_spi3cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI4_MASTER
+void nrf54l_spi4select(struct spi_dev_s *dev, uint32_t devid,
+ bool selected);
+uint8_t nrf54l_spi4status(struct spi_dev_s *dev, uint32_t devid);
+int nrf54l_spi4cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI5_MASTER
+void nrf54l_spi5select(struct spi_dev_s *dev, uint32_t devid,
+ bool selected);
+uint8_t nrf54l_spi5status(struct spi_dev_s *dev, uint32_t devid);
+int nrf54l_spi5cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI6_MASTER
+void nrf54l_spi6select(struct spi_dev_s *dev, uint32_t devid,
+ bool selected);
+uint8_t nrf54l_spi6status(struct spi_dev_s *dev, uint32_t devid);
+int nrf54l_spi6cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd);
+#endif
+
+/****************************************************************************
+ * Name: nrf54l_spi0/1/...register
+ *
+ * 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_NRF54L_SPI0_MASTER
+int nrf54l_spi0register(struct spi_dev_s *dev, spi_mediachange_t callback,
+ void *arg);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI1_MASTER
+int nrf54l_spi1register(struct spi_dev_s *dev, spi_mediachange_t callback,
+ void *arg);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI2_MASTER
+int nrf54l_spi2register(struct spi_dev_s *dev, spi_mediachange_t callback,
+ void *arg);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI3_MASTER
+int nrf54l_spi3register(struct spi_dev_s *dev, spi_mediachange_t callback,
+ void *arg);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI4_MASTER
+int nrf54l_spi4register(struct spi_dev_s *dev, spi_mediachange_t callback,
+ void *arg);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI5_MASTER
+int nrf54l_spi5register(struct spi_dev_s *dev, spi_mediachange_t callback,
+ void *arg);
+#endif
+
+#ifdef CONFIG_NRF54L_SPI6_MASTER
+int nrf54l_spi6register(struct spi_dev_s *dev, spi_mediachange_t callback,
+ void *arg);
+#endif
+#endif
+
+#endif /* __ARCH_ARM_SRC_NRF54L_NRF54L_SPI_H */