This is an automated email from the ASF dual-hosted git repository.
acassis 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 40c91176516 arm/nrf54l: add Bluetooth SoftDevice Controller support
40c91176516 is described below
commit 40c911765166f0d977c1f600fd0590c4992c6b12
Author: raiden00pl <[email protected]>
AuthorDate: Sun Sep 20 16:25:47 2026 +0200
arm/nrf54l: add Bluetooth SoftDevice Controller support
add Bluetooth SoftDevice Controller support
Assisted-by: Codex:GPT-6
Signed-off-by: raiden00pl <[email protected]>
---
Documentation/platforms/arm/nrf54l/index.rst | 23 +-
Kconfig | 2 +-
arch/arm/src/nrf54l/.gitignore | 2 +
arch/arm/src/nrf54l/CMakeLists.txt | 51 ++
arch/arm/src/nrf54l/Kconfig | 92 +++
arch/arm/src/nrf54l/Make.defs | 57 ++
arch/arm/src/nrf54l/hardware/nrf54l_power.h | 95 +++
arch/arm/src/nrf54l/nrf54l_grtc.c | 4 +
arch/arm/src/nrf54l/nrf54l_sdc.c | 1071 ++++++++++++++++++++++++++
arch/arm/src/nrf54l/nrf54l_sdc.h | 77 ++
arch/arm/src/nrf54l/nrf54l_sdc_hci.c | 405 ++++++++++
arch/arm/src/nrf54l/sdc/hal/nrf_clock.h | 35 +
arch/arm/src/nrf54l/sdc/nrf.h | 32 +
arch/arm/src/nrf54l/sdc/nrfx.h | 39 +
14 files changed, 1983 insertions(+), 2 deletions(-)
diff --git a/Documentation/platforms/arm/nrf54l/index.rst
b/Documentation/platforms/arm/nrf54l/index.rst
index c0438aaba93..025ca3d9482 100644
--- a/Documentation/platforms/arm/nrf54l/index.rst
+++ b/Documentation/platforms/arm/nrf54l/index.rst
@@ -19,7 +19,7 @@ GPIOTE No
GRTC Yes Counter and tickless scheduling
PWM No
QDEC No
-RADIO No
+RADIO Yes Bluetooth LE through SDC
RRAMC Yes Progmem erase/write interface
SAADC No
SPIM No
@@ -43,6 +43,27 @@ and twelve compare channels. ``CONFIG_NRF54L_SYSTIMER_GRTC``
reserves the
instance and compare channel zero for tickless scheduling. The GRTC is timed
by LFCLK, so the LFCLK source selected with ``CONFIG_NRF54L_USE_LFCLK``
determines the long-term accuracy of the system time.
+With SDC enabled, channels 7 through 11 are reserved for MPSL.
+
+RADIO
+-----
+
+``CONFIG_NRF54L_SOFTDEVICE_CONTROLLER`` enables Nordic's SoftDevice Controller
+using nrfxlib 3.4.1. This SDK version no longer accepts raw HCI command
+packets, so ``nrf54l_sdc_hci.c`` translates each HCI command into the
+corresponding SDC command function and builds the Command Complete or
+Command Status event. Events and ACL data pass through unchanged. Supported
+commands cover legacy advertising, scanning, central and peripheral
+connections, Data Length Extension, and 1M, 2M and Coded PHYs. The driver
+provides the NuttX Bluetooth driver interface for the native host or HCI
+transport. The build downloads nrfxlib; ``CONFIG_ALLOW_BSDNORDIC_COMPONENTS``
+must be enabled.
+
+MPSL and SDC reserve TIMER10, TIMER20, GRTC channels 7 through 11, RADIO,
+ECB00, AAR00, CCM00, CLOCK, TEMP and RRAMC, together with their DPPI/PPIB
+resources. TIMER0, TIMER6 and progmem are unavailable while SDC is enabled.
+The remaining GRTC channels can be used by the tickless scheduler. HFXO
+remains running; the LFCLK source is selected with ``CONFIG_NRF54L_USE_LFCLK``.
RRAMC
-----
diff --git a/Kconfig b/Kconfig
index a38ef5dfeb5..a97f50658bf 100644
--- a/Kconfig
+++ b/Kconfig
@@ -68,7 +68,7 @@ config ALLOW_MIT_COMPONENTS
config ALLOW_BSDNORDIC_COMPONENTS
bool "Use components that have 5-Clause Nordic licenses"
- depends on ARCH_CHIP_NRF52 || ARCH_CHIP_NRF53 || ARCH_CHIP_NRF91
+ depends on ARCH_CHIP_NRF52 || ARCH_CHIP_NRF53 || ARCH_CHIP_NRF54L ||
ARCH_CHIP_NRF91
default n
---help---
When this option is enabled the project will allow the use
diff --git a/arch/arm/src/nrf54l/.gitignore b/arch/arm/src/nrf54l/.gitignore
new file mode 100644
index 00000000000..0cbba2fac8e
--- /dev/null
+++ b/arch/arm/src/nrf54l/.gitignore
@@ -0,0 +1,2 @@
+/sdk-nrfxlib/
+/v*.tar.gz
diff --git a/arch/arm/src/nrf54l/CMakeLists.txt
b/arch/arm/src/nrf54l/CMakeLists.txt
index cc3b7f1d940..029e6a34f81 100644
--- a/arch/arm/src/nrf54l/CMakeLists.txt
+++ b/arch/arm/src/nrf54l/CMakeLists.txt
@@ -59,4 +59,55 @@ if(CONFIG_PM)
list(APPEND SRCS nrf54l_pminitialize.c)
endif()
+if(CONFIG_NRF54L_SOFTDEVICE_CONTROLLER)
+ set(NRFXLIB_VER "3.4.1")
+ set(NRFXLIB_DIR "${NUTTX_CHIP_ABS_DIR}/sdk-nrfxlib")
+
+ if(NOT EXISTS ${NRFXLIB_DIR})
+ FetchContent_Declare(
+ sdk-nrfxlib
+ URL
"https://github.com/nrfconnect/sdk-nrfxlib/archive/v${NRFXLIB_VER}.tar.gz"
+ SOURCE_DIR
+ ${NRFXLIB_DIR}
+ BINARY_DIR
+ ${NUTTX_BINARY_DIR}/arch/sdk-nrfxlib
+ DOWNLOAD_NO_PROGRESS true
+ TIMEOUT 120)
+ FetchContent_GetProperties(sdk-nrfxlib)
+ if(NOT sdk-nrfxlib_POPULATED)
+ FetchContent_Populate(sdk-nrfxlib)
+ endif()
+ endif()
+
+ list(APPEND SRCS nrf54l_sdc.c nrf54l_sdc_hci.c)
+ target_include_directories(
+ arch PRIVATE ${NUTTX_CHIP_ABS_DIR}/sdc ${NRFXLIB_DIR}/mpsl/include
+ ${NRFXLIB_DIR}/softdevice_controller/include)
+
+ if(CONFIG_ARCH_CHIP_NRF54L15)
+ target_compile_definitions(arch PRIVATE NRF54L15_XXAA)
+ set(NRFXLIB_SOC nrf54l)
+ else()
+ set(NRFXLIB_SOC nrf54lm)
+ if(CONFIG_ARCH_CHIP_NRF54LM20A)
+ target_compile_definitions(arch PRIVATE NRF54LM20A_XXAA)
+ else()
+ target_compile_definitions(arch PRIVATE NRF54LM20B_XXAA)
+ endif()
+ endif()
+
+ if(CONFIG_ARCH_FPU)
+ set(NRFXLIB_ABI hard-float)
+ else()
+ set(NRFXLIB_ABI soft-float)
+ endif()
+
+ target_link_libraries(
+ arch
+
${NRFXLIB_DIR}/softdevice_controller/lib/${NRFXLIB_SOC}/${NRFXLIB_ABI}/libsoftdevice_controller_multirole.a
+ ${NRFXLIB_DIR}/mpsl/lib/${NRFXLIB_SOC}/${NRFXLIB_ABI}/libmpsl.a
+
${NRFXLIB_DIR}/mpsl/fem/common/lib/${NRFXLIB_SOC}/${NRFXLIB_ABI}/libmpsl_fem_common.a
+ )
+endif()
+
target_sources(arch PRIVATE ${SRCS})
diff --git a/arch/arm/src/nrf54l/Kconfig b/arch/arm/src/nrf54l/Kconfig
index d124baee727..76dd4ebcbb7 100644
--- a/arch/arm/src/nrf54l/Kconfig
+++ b/arch/arm/src/nrf54l/Kconfig
@@ -103,6 +103,7 @@ config NRF54L_UART6
config NRF54L_TIMER0
bool "TIMER0 (TIMER20)"
select NRF54L_TIMER
+ depends on !NRF54L_SOFTDEVICE_CONTROLLER
default n
config NRF54L_TIMER1
@@ -133,6 +134,7 @@ config NRF54L_TIMER5
config NRF54L_TIMER6
bool "TIMER6 (TIMER10)"
select NRF54L_TIMER
+ depends on !NRF54L_SOFTDEVICE_CONTROLLER
default n
config NRF54L_GRTC
@@ -195,6 +197,7 @@ endmenu # System Timer
config NRF54L_PROGMEM
bool "RRAM program memory"
default n
+ depends on !NRF54L_SOFTDEVICE_CONTROLLER
select ARCH_HAVE_PROGMEM
---help---
Enable the program memory interfaces in nuttx/progmem.h.
@@ -206,3 +209,92 @@ config NRF54L_DEBUG_ACCESS
---help---
Open unlocked TAMPC debug signals for SWD/RTT development.
Signals locked by an earlier boot stage are left unchanged.
+
+menuconfig NRF54L_SOFTDEVICE_CONTROLLER
+ bool "SoftDevice Controller"
+ depends on ALLOW_BSDNORDIC_COMPONENTS
+ depends on NET_BLUETOOTH || UART_BTH4
+ select ARCH_RAMVECTORS
+ select ARCH_IRQPRIO
+ select ARCH_HIPRI_INTERRUPT
+ select CRYPTO
+ select CRYPTO_RANDOM_POOL
+ select SCHED_LPWORK
+ select NRF54L_GRTC
+ select NRF54L_USE_LFCLK
+ ---help---
+ Enable Nordic's Bluetooth LE SoftDevice Controller and MPSL.
+ MPSL reserves GRTC channels 7-11, TIMER10, TIMER20, ECB00,
+ RADIO, CLOCK and TEMP. SDC also reserves AAR00, CCM00 and
+ RRAMC. Their DPPI and PPIB channels must not be used by the
+ application. HFXO remains running at 32 MHz.
+ Use an interrupt stack and allow sufficient stack space for
+ controller initialization and the low-priority work queue.
+
+if NRF54L_SOFTDEVICE_CONTROLLER
+
+config NRF54L_SDC_CLOCK_ACCURACY
+ int "Clock Accuracy [PPM]"
+ default 50
+ range 20 500
+ ---help---
+ Accuracy of the board's HFXO, which supplies synthesized LFCLK.
+
+config NRF54L_SDC_PERIPHERAL_COUNT
+ int "Number of peripheral connections"
+ default 1
+ range 0 20
+
+config NRF54L_SDC_MAX_COUNT
+ int "Maximum number of connections"
+ default 2
+ range 1 20
+ depends on !NET_BLUETOOTH
+ ---help---
+ The remaining connections are central roles. With the native
+ Bluetooth host, BLUETOOTH_MAX_CONN provides this total instead.
+
+config NRF54L_SDC_ADVERTISING
+ bool "Support advertising"
+ default y
+
+config NRF54L_SDC_SCANNING
+ bool "Support scanning"
+ default y
+
+if NRF54L_SDC_SCANNING
+
+config NRF54L_SDC_SCAN_BUFFER_COUNT
+ int "Scanning buffer count"
+ default 3
+ range 2 255
+
+endif # NRF54L_SDC_SCANNING
+
+config NRF54L_SDC_LE_2M_PHY
+ bool "Support LE 2M PHY"
+ default y
+
+config NRF54L_SDC_LE_CODED_PHY
+ bool "Support LE Coded PHY"
+ default y
+
+config NRF54L_SDC_DLE
+ bool "Support Data Length Extension (DLE)"
+ default y
+
+config NRF54L_SDC_FICR_STATIC_ADDR
+ bool "Configure factory generated static random address"
+ default y
+
+config NRF54L_SDC_PUB_ADDR
+ hex "Configure Bluetooth public address"
+ default 0x000000000000
+ ---help---
+ Public address, most significant byte first.
+ For example, 0x010203040506 configures 06:05:04:03:02:01.
+ Zero leaves the controller public address unset. The native
+ NuttX Bluetooth host requires a public address; hosts using a
+ static random address configure it through HCI instead.
+
+endif # NRF54L_SOFTDEVICE_CONTROLLER
diff --git a/arch/arm/src/nrf54l/Make.defs b/arch/arm/src/nrf54l/Make.defs
index 31b12b75332..1764d2088b4 100644
--- a/arch/arm/src/nrf54l/Make.defs
+++ b/arch/arm/src/nrf54l/Make.defs
@@ -60,3 +60,60 @@ endif
ifeq ($(CONFIG_PM),y)
CHIP_CSRCS += nrf54l_pminitialize.c
endif
+
+ifeq ($(CONFIG_NRF54L_SOFTDEVICE_CONTROLLER),y)
+
+NRFXLIB_UNPACK := sdk-nrfxlib
+NRFXLIB_VER := 3.4.1
+NRFXLIB_REF := v$(NRFXLIB_VER)
+NRFXLIB_TGZ := $(NRFXLIB_REF).tar.gz
+NRFXLIB_URL := https://github.com/nrfconnect/sdk-nrfxlib/archive
+
+$(NRFXLIB_TGZ):
+ $(call DOWNLOAD,$(NRFXLIB_URL),$(NRFXLIB_TGZ),chip/$(NRFXLIB_TGZ))
+
+chip/$(NRFXLIB_UNPACK): $(NRFXLIB_TGZ)
+ $(Q) cd chip && tar zxf $(NRFXLIB_TGZ)
+ $(Q) mv chip/$(NRFXLIB_UNPACK)-$(NRFXLIB_VER) chip/$(NRFXLIB_UNPACK)
+ $(Q) touch chip/$(NRFXLIB_UNPACK)
+
+ifeq ($(wildcard chip/$(NRFXLIB_UNPACK)/.git),)
+context:: chip/$(NRFXLIB_UNPACK)
+
+distclean::
+ $(call DELFILE, chip/$(NRFXLIB_TGZ))
+ $(call DELDIR, chip/$(NRFXLIB_UNPACK))
+endif
+
+CHIP_CSRCS += nrf54l_sdc.c nrf54l_sdc_hci.c
+
+NRFXLIB_DIR = $(ARCH_SRCDIR)$(DELIM)chip$(DELIM)sdk-nrfxlib
+
+INCLUDES += ${INCDIR_PREFIX}$(ARCH_SRCDIR)$(DELIM)chip$(DELIM)sdc
+INCLUDES += ${INCDIR_PREFIX}$(NRFXLIB_DIR)$(DELIM)mpsl$(DELIM)include
+INCLUDES +=
${INCDIR_PREFIX}$(NRFXLIB_DIR)$(DELIM)softdevice_controller$(DELIM)include
+
+ifeq ($(CONFIG_ARCH_CHIP_NRF54L15),y)
+CFLAGS += -DNRF54L15_XXAA
+NRFXLIB_SOC = nrf54l
+else
+NRFXLIB_SOC = nrf54lm
+ifeq ($(CONFIG_ARCH_CHIP_NRF54LM20A),y)
+CFLAGS += -DNRF54LM20A_XXAA
+else
+CFLAGS += -DNRF54LM20B_XXAA
+endif
+endif
+
+ifeq ($(CONFIG_ARCH_FPU),y)
+NRFXLIB_ABI = hard-float
+else
+NRFXLIB_ABI = soft-float
+endif
+
+EXTRA_LIBPATHS += -L $(NRFXLIB_DIR)/mpsl/lib/$(NRFXLIB_SOC)/$(NRFXLIB_ABI)
+EXTRA_LIBPATHS += -L
$(NRFXLIB_DIR)/softdevice_controller/lib/$(NRFXLIB_SOC)/$(NRFXLIB_ABI)
+EXTRA_LIBPATHS += -L
$(NRFXLIB_DIR)/mpsl/fem/common/lib/$(NRFXLIB_SOC)/$(NRFXLIB_ABI)
+EXTRA_LIBS += -lsoftdevice_controller_multirole -lmpsl -lmpsl_fem_common
+
+endif
diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_power.h
b/arch/arm/src/nrf54l/hardware/nrf54l_power.h
new file mode 100644
index 00000000000..c4c03878f9b
--- /dev/null
+++ b/arch/arm/src/nrf54l/hardware/nrf54l_power.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/hardware/nrf54l_power.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_POWER_H
+#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_POWER_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "nrf54l_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define NRF54L_POWER_TASKS_CONSTLAT_OFFSET 0x000030 /* Enable constant
latency mode */
+#define NRF54L_POWER_TASKS_LOWPWR_OFFSET 0x000034 /* Enable low power
mode */
+#define NRF54L_POWER_SUBSCRIBE_CONSTLAT_OFFSET 0x0000b0 /* Subscribe to
CONSTLAT */
+#define NRF54L_POWER_SUBSCRIBE_LOWPWR_OFFSET 0x0000b4 /* Subscribe to
LOWPWR */
+#define NRF54L_POWER_EVENTS_POFWARN_OFFSET 0x000130 /* Power failure
warning */
+#define NRF54L_POWER_EVENTS_SLEEPENTER_OFFSET 0x000134 /* CPU entered
WFI/WFE sleep */
+#define NRF54L_POWER_EVENTS_SLEEPEXIT_OFFSET 0x000138 /* CPU exited WFI/WFE
sleep */
+#define NRF54L_POWER_PUBLISH_POFWARN_OFFSET 0x0001b0 /* Publish POFWARN */
+#define NRF54L_POWER_PUBLISH_SLEEPENTER_OFFSET 0x0001b4 /* Publish SLEEPENTER
*/
+#define NRF54L_POWER_PUBLISH_SLEEPEXIT_OFFSET 0x0001b8 /* Publish SLEEPEXIT
*/
+#define NRF54L_POWER_INTEN_OFFSET 0x000300 /* Enable or disable
interrupt */
+#define NRF54L_POWER_INTENSET_OFFSET 0x000304 /* Enable interrupt */
+#define NRF54L_POWER_INTENCLR_OFFSET 0x000308 /* Disable interrupt
*/
+#define NRF54L_POWER_GPREGRET0_OFFSET 0x000500 /* General purpose
retention register 0 */
+#define NRF54L_POWER_GPREGRET1_OFFSET 0x000504 /* General purpose
retention register 1 */
+#define NRF54L_POWER_CONSTLATSTAT_OFFSET 0x000520 /* Constant latency
status */
+
+/* Register definitions *****************************************************/
+
+#define NRF54L_POWER_TASKS_CONSTLAT (NRF54L_POWER_BASE +
NRF54L_POWER_TASKS_CONSTLAT_OFFSET)
+#define NRF54L_POWER_TASKS_LOWPWR (NRF54L_POWER_BASE +
NRF54L_POWER_TASKS_LOWPWR_OFFSET)
+#define NRF54L_POWER_SUBSCRIBE_CONSTLAT (NRF54L_POWER_BASE +
NRF54L_POWER_SUBSCRIBE_CONSTLAT_OFFSET)
+#define NRF54L_POWER_SUBSCRIBE_LOWPWR (NRF54L_POWER_BASE +
NRF54L_POWER_SUBSCRIBE_LOWPWR_OFFSET)
+#define NRF54L_POWER_EVENTS_POFWARN (NRF54L_POWER_BASE +
NRF54L_POWER_EVENTS_POFWARN_OFFSET)
+#define NRF54L_POWER_EVENTS_SLEEPENTER (NRF54L_POWER_BASE +
NRF54L_POWER_EVENTS_SLEEPENTER_OFFSET)
+#define NRF54L_POWER_EVENTS_SLEEPEXIT (NRF54L_POWER_BASE +
NRF54L_POWER_EVENTS_SLEEPEXIT_OFFSET)
+#define NRF54L_POWER_PUBLISH_POFWARN (NRF54L_POWER_BASE +
NRF54L_POWER_PUBLISH_POFWARN_OFFSET)
+#define NRF54L_POWER_PUBLISH_SLEEPENTER (NRF54L_POWER_BASE +
NRF54L_POWER_PUBLISH_SLEEPENTER_OFFSET)
+#define NRF54L_POWER_PUBLISH_SLEEPEXIT (NRF54L_POWER_BASE +
NRF54L_POWER_PUBLISH_SLEEPEXIT_OFFSET)
+#define NRF54L_POWER_INTEN (NRF54L_POWER_BASE +
NRF54L_POWER_INTEN_OFFSET)
+#define NRF54L_POWER_INTENSET (NRF54L_POWER_BASE +
NRF54L_POWER_INTENSET_OFFSET)
+#define NRF54L_POWER_INTENCLR (NRF54L_POWER_BASE +
NRF54L_POWER_INTENCLR_OFFSET)
+#define NRF54L_POWER_GPREGRET0 (NRF54L_POWER_BASE +
NRF54L_POWER_GPREGRET0_OFFSET)
+#define NRF54L_POWER_GPREGRET1 (NRF54L_POWER_BASE +
NRF54L_POWER_GPREGRET1_OFFSET)
+#define NRF54L_POWER_CONSTLATSTAT (NRF54L_POWER_BASE +
NRF54L_POWER_CONSTLATSTAT_OFFSET)
+
+/* Register bit definitions *************************************************/
+
+#define POWER_TASKS_CONSTLAT (1 << 0)
+#define POWER_TASKS_LOWPWR (1 << 0)
+#define POWER_EVENTS_POFWARN (1 << 0)
+#define POWER_EVENTS_SLEEPENTER (1 << 0)
+#define POWER_EVENTS_SLEEPEXIT (1 << 0)
+
+#define POWER_INT_POFWARN (1 << 12)
+#define POWER_INT_SLEEPENTER (1 << 13)
+#define POWER_INT_SLEEPEXIT (1 << 14)
+
+#define POWER_SUBSCRIBE_CHIDX_MASK (0xff)
+#define POWER_SUBSCRIBE_EN (1 << 31)
+#define POWER_PUBLISH_CHIDX_MASK (0xff)
+#define POWER_PUBLISH_EN (1 << 31)
+
+#define POWER_GPREGRET_MASK (0xff)
+#define POWER_CONSTLATSTAT_STATUS (1 << 0)
+
+#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_POWER_H */
diff --git a/arch/arm/src/nrf54l/nrf54l_grtc.c
b/arch/arm/src/nrf54l/nrf54l_grtc.c
index d34972239f1..41f08f76bcb 100644
--- a/arch/arm/src/nrf54l/nrf54l_grtc.c
+++ b/arch/arm/src/nrf54l/nrf54l_grtc.c
@@ -122,7 +122,11 @@ struct nrf54l_grtc_priv_s g_nrf54l_grtc_priv =
.ops = &nrf54l_grtc_ops,
.base = NRF54L_GRTC_BASE,
.irq = NRF54L_IRQ_GRTC_0,
+#ifdef CONFIG_NRF54L_SOFTDEVICE_CONTROLLER
+ .chan = 7,
+#else
.chan = 12,
+#endif
.inuse = false,
};
diff --git a/arch/arm/src/nrf54l/nrf54l_sdc.c b/arch/arm/src/nrf54l/nrf54l_sdc.c
new file mode 100644
index 00000000000..27ea0875425
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_sdc.c
@@ -0,0 +1,1071 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_sdc.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 <nuttx/debug.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <nuttx/net/bluetooth.h>
+#include <nuttx/wireless/bluetooth/bt_hci.h>
+#include <nuttx/wireless/bluetooth/bt_driver.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <arch/armv8-m/nvicpri.h>
+#include <nuttx/wqueue.h>
+
+#include "arm_internal.h"
+#include "ram_vectors.h"
+
+#include "hardware/nrf54l_ficr.h"
+#include "hardware/nrf54l_rramc.h"
+#include "hardware/nrf54l_power.h"
+#include "hardware/nrf54l_clock.h"
+#include "nrf54l_grtc.h"
+#include "nrf54l_sdc.h"
+
+#include <mpsl.h>
+#include <sdc.h>
+#include <sdc_hci.h>
+#include <sdc_soc.h>
+#include <sdc_hci_cmd_le.h>
+#include <sdc_hci_cmd_controller_baseband.h>
+#include <sdc_hci_vs.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Connections configuration ************************************************/
+
+#if NRF54L_SDC_CENTRAL_COUNT < 0
+# error "Cannot support more BLE peripheral roles than connections"
+#endif
+
+/* Memory configuration *****************************************************/
+
+/* Observer configuration */
+
+#ifdef CONFIG_NRF54L_SDC_SCANNING
+# if CONFIG_NRF54L_SDC_SCAN_BUFFER_COUNT < 2
+# error The minimum allowed number of scan buffers is 2.
+# endif
+# define SCAN_MEM_COUNT (CONFIG_NRF54L_SDC_SCAN_BUFFER_COUNT)
+#elif NRF54L_SDC_CENTRAL_COUNT > 0
+# define SCAN_MEM_COUNT (3)
+#else
+# define SCAN_MEM_COUNT (0)
+#endif
+
+/* Broadcaster configuration */
+
+#ifdef CONFIG_NRF54L_SDC_ADVERTISING
+/* Advertising extensions not supported for now */
+
+# define ADV_SET_COUNT (1)
+# define ADV_BUF_SIZE (SDC_DEFAULT_ADV_BUF_SIZE)
+#else
+# define ADV_SET_COUNT (0)
+# define ADV_BUF_SIZE (0)
+#endif
+
+/* BT address configuration *************************************************/
+
+#if (CONFIG_NRF54L_SDC_PUB_ADDR > 0) || \
+ defined(CONFIG_NRF54L_SDC_FICR_STATIC_ADDR)
+# define HAVE_BTADDR_CONFIGURE
+#endif
+
+/* Calls to MPSL ************************************************************/
+
+#define MPSL_IRQ_CLOCK_HANDLER MPSL_IRQ_CLOCK_Handler
+#define MPSL_IRQ_RTC0_HANDLER MPSL_IRQ_RTC0_Handler
+#define MPSL_IRQ_TIMER0_HANDLER MPSL_IRQ_TIMER0_Handler
+#define MPSL_IRQ_RADIO_HANDLER MPSL_IRQ_RADIO_Handler
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nrf54l_sdc_dev_s
+{
+ uint8_t *mempool; /* Must be 8 bytes aligned */
+
+ mutex_t lock;
+ struct work_s work;
+ struct work_s hci_work;
+ bool opened;
+ bool initialized;
+ bool initializing;
+ bool mpsl_initialized;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void mpsl_assert_handler(const char *const file, const uint32_t line);
+static void sdc_fault_handler(const char *file, const uint32_t line);
+
+static int bt_open(struct bt_driver_s *btdev);
+static int bt_hci_send(struct bt_driver_s *btdev,
+ enum bt_buf_type_e type,
+ void *data, size_t len);
+
+static void on_hci(void);
+static void on_hci_worker(void *arg);
+
+static void low_prio_worker(void *arg);
+
+static int swi_isr(int irq, void *context, void *arg);
+static int power_clock_isr(int irq, void *context, void *arg);
+
+static void rtc0_handler(void);
+static void timer0_handler(void);
+static void radio_handler(void);
+static int32_t nrf54l_sdc_lfclk_request(void);
+static int32_t nrf54l_sdc_lfclk_release(void);
+static void nrf54l_sdc_hfclk_request(void);
+static void nrf54l_sdc_hfclk_release(void);
+static bool nrf54l_sdc_hfclk_running(void);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct bt_driver_s g_bt_driver =
+{
+ .head_reserve = 0,
+ .open = bt_open,
+ .send = bt_hci_send
+};
+
+static const mpsl_clock_lfclk_ctrl_source_t g_lfclk_source =
+{
+ .lfclk_wait = nrf54l_sdc_lfclk_request,
+ .lfclk_request = nrf54l_sdc_lfclk_request,
+ .lfclk_release = nrf54l_sdc_lfclk_release,
+ .accuracy_ppm = CONFIG_NRF54L_SDC_CLOCK_ACCURACY,
+ .skip_wait_lfclk_started = false
+};
+
+static const mpsl_clock_hfclk_ctrl_source_t g_hfclk_source =
+{
+ .hfclk_request = nrf54l_sdc_hfclk_request,
+ .hfclk_release = nrf54l_sdc_hfclk_release,
+ .hfclk_is_running = nrf54l_sdc_hfclk_running,
+ .startup_time_us = 1
+};
+
+static uint32_t g_rram_lowpower;
+static bool g_constlat;
+static bool g_low_latency;
+
+static struct nrf54l_sdc_dev_s g_sdc_dev =
+{
+ .lock = NXMUTEX_INITIALIZER,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_sdc_lfclk_request
+ ****************************************************************************/
+
+static int32_t nrf54l_sdc_lfclk_request(void)
+{
+ /* Startup keeps synthesized LFCLK running for both GRTC and MPSL. */
+
+ return (getreg32(NRF54L_CLOCK_LFCLK_STAT) & CLOCK_LFCLK_STAT_STATE) != 0 ?
+ OK : -EIO;
+}
+
+/****************************************************************************
+ * Name: nrf54l_sdc_lfclk_release
+ ****************************************************************************/
+
+static int32_t nrf54l_sdc_lfclk_release(void)
+{
+ /* GRTC still uses this clock when the controller releases its request. */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nrf54l_sdc_hfclk_request
+ ****************************************************************************/
+
+static void nrf54l_sdc_hfclk_request(void)
+{
+ DEBUGASSERT(nrf54l_sdc_hfclk_running());
+}
+
+/****************************************************************************
+ * Name: nrf54l_sdc_hfclk_release
+ ****************************************************************************/
+
+static void nrf54l_sdc_hfclk_release(void)
+{
+ /* The CPU and synthesized LFCLK retain the startup HFXO request. */
+}
+
+/****************************************************************************
+ * Name: nrf54l_sdc_hfclk_running
+ ****************************************************************************/
+
+static bool nrf54l_sdc_hfclk_running(void)
+{
+ return (getreg32(NRF54L_CLOCK_XO_STAT) & CLOCK_XO_STAT_STATE) != 0;
+}
+
+/****************************************************************************
+ * Name: bt_open
+ ****************************************************************************/
+
+static int bt_open(struct bt_driver_s *btdev)
+{
+ g_sdc_dev.opened = true;
+ on_hci();
+ return 0;
+}
+
+/****************************************************************************
+ * Name: bt_hci_send
+ ****************************************************************************/
+
+static int bt_hci_send(struct bt_driver_s *btdev,
+ enum bt_buf_type_e type,
+ void *data, size_t len)
+{
+ const uint8_t *packet = data;
+ uint8_t event[HCI_EVENT_PACKET_MAX_SIZE];
+ size_t eventlen = 0;
+ int ret = -EIO;
+
+ if (data == NULL ||
+ (type == BT_CMD && (len < 3 || len != 3 + packet[2] ||
+ len > HCI_CMD_PACKET_MAX_SIZE)) ||
+ (type == BT_ACL_OUT &&
+ (len < 4 || len > HCI_DATA_PACKET_MAX_SIZE ||
+ len != 4 + (packet[2] | (packet[3] << 8)))))
+ {
+ return -EINVAL;
+ }
+
+ /* Pass HCI CMD/DATA to SDC */
+
+ if (type == BT_CMD || type == BT_ACL_OUT)
+ {
+ wlinfo("passing type %s to softdevice\n",
+ (type == BT_CMD) ? "CMD" : "ACL");
+
+ /* Ensure non-concurrent access to SDC operations */
+
+ ret = nxmutex_lock(&g_sdc_dev.lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (type == BT_CMD)
+ {
+ eventlen = nrf54l_sdc_command(data, event);
+ ret = OK;
+ }
+ else
+ {
+ ret = sdc_hci_data_put(data);
+ }
+
+ nxmutex_unlock(&g_sdc_dev.lock);
+ if (eventlen > 0)
+ {
+ bt_netdev_receive(&g_bt_driver, BT_EVT, event, eventlen);
+ }
+
+ if (ret >= 0)
+ {
+ ret = len;
+
+ work_queue(LPWORK, &g_sdc_dev.hci_work, on_hci_worker, NULL, 0);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: sdc_fault_handler
+ ****************************************************************************/
+
+static void sdc_fault_handler(const char *file, const uint32_t line)
+{
+ __assert(file, line, "SoftDevice Controller Fault");
+}
+
+/****************************************************************************
+ * Name: mpsl_assert_handler
+ ****************************************************************************/
+
+static void mpsl_assert_handler(const char *const file, const uint32_t line)
+{
+ __assert(file, line, "MPSL assertion failed");
+}
+
+/****************************************************************************
+ * Name: low_prio_worker
+ ****************************************************************************/
+
+static void low_prio_worker(void *arg)
+{
+ /* Invoke MPSL low priority process handler. This will call on_hci()
+ * internally when required.
+ */
+
+ nxmutex_lock(&g_sdc_dev.lock);
+ if (g_sdc_dev.mpsl_initialized)
+ {
+ mpsl_low_priority_process();
+ }
+
+ nxmutex_unlock(&g_sdc_dev.lock);
+}
+
+/****************************************************************************
+ * Name: on_hci_worker
+ ****************************************************************************/
+
+static void on_hci_worker(void *arg)
+{
+ uint8_t msg_buffer[HCI_MSG_BUFFER_MAX_SIZE];
+ sdc_hci_msg_type_t type;
+ bool check_again;
+ size_t len;
+ int ret;
+
+ do
+ {
+ check_again = false;
+
+ /* Check for EVT by trying to get pending data into a generic
+ * buffer and then create an actual bt_buf_s, depending on msg length
+ */
+
+ nxmutex_lock(&g_sdc_dev.lock);
+ if (!g_sdc_dev.opened)
+ {
+ nxmutex_unlock(&g_sdc_dev.lock);
+ return;
+ }
+
+ ret = sdc_hci_get(msg_buffer, &type);
+ nxmutex_unlock(&g_sdc_dev.lock);
+
+ if (ret == 0)
+ {
+ if (type == SDC_HCI_MSG_TYPE_EVT)
+ {
+ struct bt_hci_evt_hdr_s *hdr =
+ (struct bt_hci_evt_hdr_s *)msg_buffer;
+
+ len = sizeof(*hdr) + hdr->len;
+
+#ifdef CONFIG_DEBUG_WIRELESS_INFO
+ if (hdr->evt == BT_HCI_EVT_CMD_COMPLETE)
+ {
+ struct hci_evt_cmd_complete_s *cmd_complete =
+ (struct hci_evt_cmd_complete_s *)
+ (msg_buffer + sizeof(*hdr));
+ uint8_t *status = (uint8_t *)cmd_complete + 3;
+
+ wlinfo("received CMD_COMPLETE from softdevice "
+ "(opcode: 0x%x, status: 0x%x)\n",
+ cmd_complete->opcode, *status);
+ }
+ else
+ {
+ wlinfo("received HCI EVT from softdevice "
+ "(evt: %d, len: %zu)\n", hdr->evt, len);
+ }
+#endif
+
+ bt_netdev_receive(&g_bt_driver, BT_EVT,
+ msg_buffer, len);
+ check_again = true;
+ }
+
+ if (type == SDC_HCI_MSG_TYPE_DATA)
+ {
+ struct bt_hci_acl_hdr_s *hdr =
+ (struct bt_hci_acl_hdr_s *)msg_buffer;
+
+ wlinfo("received HCI ACL from softdevice (handle: %d)\n",
+ hdr->handle);
+
+ len = sizeof(*hdr) + hdr->len;
+
+ bt_netdev_receive(&g_bt_driver, BT_ACL_IN,
+ msg_buffer, len);
+ check_again = true;
+ }
+ }
+ }
+ while (check_again);
+}
+
+/****************************************************************************
+ * Name: on_hci
+ *
+ * Description:
+ * SDC message callback.
+ *
+ ****************************************************************************/
+
+static void on_hci(void)
+{
+ work_queue(LPWORK, &g_sdc_dev.hci_work, on_hci_worker, NULL, 0);
+}
+
+/****************************************************************************
+ * Name: swi_isr
+ ****************************************************************************/
+
+static int swi_isr(int irq, void *context, void *arg)
+{
+ work_queue(LPWORK, &g_sdc_dev.work, low_prio_worker, NULL, 0);
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: power_clock_isr
+ ****************************************************************************/
+
+static int power_clock_isr(int irq, void *context, void *arg)
+{
+ MPSL_IRQ_CLOCK_HANDLER();
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: rtc0_handler
+ ****************************************************************************/
+
+static void rtc0_handler(void)
+{
+ MPSL_IRQ_RTC0_HANDLER();
+}
+
+/****************************************************************************
+ * Name: timer0_handler
+ ****************************************************************************/
+
+static void timer0_handler(void)
+{
+ MPSL_IRQ_TIMER0_HANDLER();
+}
+
+/****************************************************************************
+ * Name: radio_handler
+ ****************************************************************************/
+
+static void radio_handler(void)
+{
+ MPSL_IRQ_RADIO_HANDLER();
+}
+
+#ifdef HAVE_BTADDR_CONFIGURE
+/****************************************************************************
+ * Name: nrf54l_sdc_btaddr_configure
+ ****************************************************************************/
+
+static int nrf54l_sdc_btaddr_configure(void)
+{
+#if CONFIG_NRF54L_SDC_PUB_ADDR > 0
+ sdc_hci_cmd_vs_zephyr_write_bd_addr_t pub_addr;
+#endif
+#ifdef CONFIG_NRF54L_SDC_FICR_STATIC_ADDR
+ sdc_hci_cmd_le_set_random_address_t rand_addr;
+ uint32_t addr[2];
+ uint32_t addrtype = 0;
+#endif
+ int ret = OK;
+
+#ifdef CONFIG_NRF54L_SDC_FICR_STATIC_ADDR
+ /* Get device address type */
+
+ addrtype = getreg32(NRF54L_FICR_DEVICEADDRTYPE);
+
+ /* Get device addr from FICR */
+
+ addr[0] = getreg32(NRF54L_FICR_DEVICEADDR(0));
+ addr[1] = getreg32(NRF54L_FICR_DEVICEADDR(1));
+
+ if ((addrtype & 0x01) == FICR_DEVICEADDRTYPE_RANDOM)
+ {
+ /* Configure static random address */
+
+ memcpy(&rand_addr.random_address[0], &addr[0], 4);
+ memcpy(&rand_addr.random_address[4], &addr[1], 2);
+
+ /* The two most significant bits of the address shall be set */
+
+ rand_addr.random_address[5] |= 0xc0;
+
+ ret = sdc_hci_cmd_le_set_random_address(&rand_addr);
+ if (ret != 0)
+ {
+ wlerr("sdc_hci_cmd_le_set_random_address failed: %d\n", ret);
+ ret = -EIO;
+ goto errout;
+ }
+ }
+ else
+ {
+ wlerr("Static random address not available\n");
+ ret = -EINVAL;
+ goto errout;
+ }
+#endif
+
+#if CONFIG_NRF54L_SDC_PUB_ADDR > 0
+ /* Configure public address if available */
+
+ pub_addr.bd_addr[0] = (CONFIG_NRF54L_SDC_PUB_ADDR >> (8 * 5)) & 0xff;
+ pub_addr.bd_addr[1] = (CONFIG_NRF54L_SDC_PUB_ADDR >> (8 * 4)) & 0xff;
+ pub_addr.bd_addr[2] = (CONFIG_NRF54L_SDC_PUB_ADDR >> (8 * 3)) & 0xff;
+ pub_addr.bd_addr[3] = (CONFIG_NRF54L_SDC_PUB_ADDR >> (8 * 2)) & 0xff;
+ pub_addr.bd_addr[4] = (CONFIG_NRF54L_SDC_PUB_ADDR >> (8 * 1)) & 0xff;
+ pub_addr.bd_addr[5] = (CONFIG_NRF54L_SDC_PUB_ADDR >> (8 * 0)) & 0xff;
+
+ ret = sdc_hci_cmd_vs_zephyr_write_bd_addr(&pub_addr);
+ if (ret != 0)
+ {
+ wlerr("sdc_hci_cmd_vs_zephyr_write_bd_addr failed: %d\n", ret);
+ ret = -EIO;
+ goto errout;
+ }
+#endif
+
+errout:
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: nrf54l_rand_poll
+ ****************************************************************************/
+
+static void nrf54l_rand_poll(uint8_t *p_buff, uint8_t length)
+{
+ arc4random_buf(p_buff, length);
+}
+
+/****************************************************************************
+ * Name: nrf54l_configure_features
+ ****************************************************************************/
+
+static int nrf54l_configure_features(void)
+{
+ /* Turn on specific features */
+
+#ifdef CONFIG_NRF54L_SDC_ADVERTISING
+ sdc_support_adv();
+#endif
+
+#if defined(CONFIG_NRF54L_SDC_SCANNING) && NRF54L_SDC_CENTRAL_COUNT == 0
+ sdc_support_scan();
+#endif
+
+#if NRF54L_SDC_CENTRAL_COUNT > 0
+ sdc_support_central();
+#endif
+
+#if CONFIG_NRF54L_SDC_PERIPHERAL_COUNT > 0
+ sdc_support_peripheral();
+#endif
+
+#ifdef CONFIG_NRF54L_SDC_DLE
+# if NRF54L_SDC_CENTRAL_COUNT > 0
+ sdc_support_dle_central();
+# endif
+
+# if CONFIG_NRF54L_SDC_PERIPHERAL_COUNT > 0
+ sdc_support_dle_peripheral();
+# endif
+#endif
+
+#ifdef CONFIG_NRF54L_SDC_LE_2M_PHY
+ sdc_support_le_2m_phy();
+#endif
+
+#ifdef CONFIG_NRF54L_SDC_LE_CODED_PHY
+ sdc_support_le_coded_phy();
+#endif
+
+#if defined(CONFIG_NRF54L_SDC_LE_2M_PHY) || \
+ defined(CONFIG_NRF54L_SDC_LE_CODED_PHY)
+# if NRF54L_SDC_CENTRAL_COUNT > 0
+ sdc_support_phy_update_central();
+# endif
+# if CONFIG_NRF54L_SDC_PERIPHERAL_COUNT > 0
+ sdc_support_phy_update_peripheral();
+# endif
+#endif
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nrf54l_configure_memory
+ ****************************************************************************/
+
+static int nrf54l_configure_memory(void)
+{
+ int32_t required_memory = 0;
+ int ret = OK;
+ sdc_cfg_t cfg;
+
+ /* Configure SoftDevice memory.
+ * sdc_support_*() calls must precede sdc_cfg_set() and sdc_enable().
+ */
+
+ /* Configure scanner memory */
+
+#if SCAN_MEM_COUNT > 0
+ cfg.scan_buffer_cfg.count = SCAN_MEM_COUNT;
+ ret = sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
+ SDC_CFG_TYPE_SCAN_BUFFER_CFG, &cfg);
+ if (ret < 0)
+ {
+ wlerr("Failed to set scan count: %d\n", ret);
+ goto errout;
+ }
+#endif
+
+ /* Configure advertisers memory */
+
+ cfg.adv_count.count = ADV_SET_COUNT;
+ ret = sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
+ SDC_CFG_TYPE_ADV_COUNT, &cfg);
+ if (ret < 0)
+ {
+ wlerr("Failed to set advertising count: %d\n", ret);
+ goto errout;
+ }
+
+#if ADV_SET_COUNT > 0
+ cfg.adv_buffer_cfg.max_adv_data = ADV_BUF_SIZE;
+ ret = sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
+ SDC_CFG_TYPE_ADV_BUFFER_CFG, &cfg);
+ if (ret < 0)
+ {
+ wlerr("Failed to set advertising buffer: %d\n", ret);
+ goto errout;
+ }
+#endif
+
+ /* Configure central connections memory */
+
+ cfg.central_count.count = NRF54L_SDC_CENTRAL_COUNT;
+ ret = sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
+ SDC_CFG_TYPE_CENTRAL_COUNT, &cfg);
+ if (ret < 0)
+ {
+ wlerr("Failed to set central role count: %d\n", ret);
+ goto errout;
+ }
+
+ /* Configure peripheral connections memory */
+
+ cfg.peripheral_count.count = CONFIG_NRF54L_SDC_PERIPHERAL_COUNT;
+ ret = sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
+ SDC_CFG_TYPE_PERIPHERAL_COUNT, &cfg);
+ if (ret < 0)
+ {
+ wlerr("Failed to set peripheral role count: %d\n", ret);
+ goto errout;
+ }
+
+ /* Configure buffers memory and get the final required memory */
+
+ cfg.buffer_cfg.rx_packet_size = SDC_DEFAULT_RX_PACKET_SIZE;
+ cfg.buffer_cfg.tx_packet_size = SDC_DEFAULT_TX_PACKET_SIZE;
+ cfg.buffer_cfg.rx_packet_count = SDC_DEFAULT_RX_PACKET_COUNT;
+ cfg.buffer_cfg.tx_packet_count = SDC_DEFAULT_TX_PACKET_COUNT;
+
+ required_memory =
+ sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
+ SDC_CFG_TYPE_BUFFER_CFG, &cfg);
+
+ if (required_memory < 0)
+ {
+ wlerr("Failed to set packet size/count: %ld\n", required_memory);
+ ret = required_memory;
+ goto errout;
+ }
+
+ /* Use the exact size returned by this version of the controller. */
+
+ g_sdc_dev.mempool = kmm_memalign(8, required_memory);
+ ret = OK;
+ if (g_sdc_dev.mempool == NULL)
+ {
+ ret = -ENOMEM;
+ }
+ else
+ {
+ memset(g_sdc_dev.mempool, 0, required_memory);
+ }
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_configure_rand
+ ****************************************************************************/
+
+static int nrf54l_configure_rand(void)
+{
+ sdc_rand_source_t rand_func;
+ int ret = OK;
+
+ /* Enable rand source */
+
+ rand_func.rand_poll = nrf54l_rand_poll;
+
+ ret = sdc_rand_source_register(&rand_func);
+ if (ret < 0)
+ {
+ wlerr("sdc_rand_source_register failed %d\n", ret);
+ goto errout;
+ }
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_sdc_reset
+ *
+ * Description:
+ * Reset the controller and restore the configured identity address.
+ * Called with the controller mutex held; returns an HCI status byte.
+ *
+ ****************************************************************************/
+
+uint8_t nrf54l_sdc_reset(void)
+{
+ uint8_t status = sdc_hci_cmd_cb_reset();
+
+#ifdef HAVE_BTADDR_CONFIGURE
+ if (status == 0 && nrf54l_sdc_btaddr_configure() < 0)
+ {
+ status = 0x1f; /* Unspecified Error */
+ }
+#endif
+
+ return status;
+}
+
+/****************************************************************************
+ * Name: mpsl_low_latency_acquire_callback
+ ****************************************************************************/
+
+void mpsl_low_latency_acquire_callback(void)
+{
+ /* MPSL can coalesce adjacent windows and omit intermediate releases. */
+
+ if (!g_low_latency)
+ {
+ g_rram_lowpower = getreg32(NRF54L_RRAMC_POWER_LOWPOWERCONFIG);
+ g_constlat = (getreg32(NRF54L_POWER_CONSTLATSTAT) &
+ POWER_CONSTLATSTAT_STATUS) != 0;
+ g_low_latency = true;
+ }
+
+ putreg32(POWER_TASKS_CONSTLAT, NRF54L_POWER_TASKS_CONSTLAT);
+ putreg32((g_rram_lowpower & ~RRAMC_LOWPOWER_MODE_MASK) |
+ RRAMC_LOWPOWER_STANDBY, NRF54L_RRAMC_POWER_LOWPOWERCONFIG);
+}
+
+/****************************************************************************
+ * Name: mpsl_low_latency_release_callback
+ ****************************************************************************/
+
+void mpsl_low_latency_release_callback(void)
+{
+ if (g_low_latency)
+ {
+ putreg32(g_rram_lowpower, NRF54L_RRAMC_POWER_LOWPOWERCONFIG);
+ if (!g_constlat)
+ {
+ putreg32(POWER_TASKS_LOWPWR, NRF54L_POWER_TASKS_LOWPWR);
+ }
+
+ g_low_latency = false;
+ }
+}
+
+/****************************************************************************
+ * Name: nrf54l_sdc_initialize
+ ****************************************************************************/
+
+int nrf54l_sdc_initialize(void)
+{
+#ifndef CONFIG_NRF54L_SYSTIMER_GRTC
+ struct nrf54l_grtc_dev_s *grtc;
+#endif
+ int ret = OK;
+
+ ret = nxmutex_lock(&g_sdc_dev.lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (g_sdc_dev.initialized || g_sdc_dev.initializing)
+ {
+ ret = g_sdc_dev.initialized ? OK : -EBUSY;
+ nxmutex_unlock(&g_sdc_dev.lock);
+ return ret;
+ }
+
+ g_sdc_dev.initializing = true;
+
+ ret = mpsl_clock_ctrl_source_register(&g_lfclk_source, &g_hfclk_source);
+ if (ret < 0)
+ {
+ wlerr("mpsl_clock_ctrl_source_register failed: %d\n", ret);
+ g_sdc_dev.initializing = false;
+ nxmutex_unlock(&g_sdc_dev.lock);
+ return ret;
+ }
+
+#ifndef CONFIG_NRF54L_SYSTIMER_GRTC
+ grtc = nrf54l_grtc_init(0);
+ if (grtc == NULL)
+ {
+ ret = -EBUSY;
+ goto errout_clocks;
+ }
+
+ ret = NRF54L_GRTC_START(grtc);
+ if (ret < 0)
+ {
+ goto errout_grtc;
+ }
+#endif
+
+ /* Register interrupt handler for normal-priority events. SWI3 will be
+ * used by MPSL to delegate low-priority work
+ */
+
+ ret = irq_attach(NRF54L_IRQ_SWI03, swi_isr, NULL);
+ if (ret < 0)
+ {
+ goto errout_grtc;
+ }
+
+ ret = irq_attach(NRF54L_IRQ_CLOCK_POWER, power_clock_isr, NULL);
+ if (ret < 0)
+ {
+ goto errout_swi;
+ }
+
+ up_enable_irq(NRF54L_IRQ_SWI03);
+ up_enable_irq(NRF54L_IRQ_CLOCK_POWER);
+
+ up_prioritize_irq(NRF54L_IRQ_SWI03, NVIC_SYSH_PRIORITY_DEFAULT);
+ up_prioritize_irq(NRF54L_IRQ_CLOCK_POWER, NVIC_SYSH_PRIORITY_DEFAULT);
+
+ /* Register high-priority interrupts for specific peripherals */
+
+ ret = arm_ramvec_attach(NRF54L_IRQ_GRTC_3, rtc0_handler);
+ if (ret < 0)
+ {
+ goto errout_power;
+ }
+
+ ret = arm_ramvec_attach(NRF54L_IRQ_TIMER10, timer0_handler);
+ if (ret < 0)
+ {
+ goto errout_rtc;
+ }
+
+ ret = arm_ramvec_attach(NRF54L_IRQ_RADIO_0, radio_handler);
+ if (ret < 0)
+ {
+ goto errout_timer;
+ }
+
+ up_prioritize_irq(NRF54L_IRQ_GRTC_3, MPSL_HIGH_IRQ_PRIORITY);
+ up_prioritize_irq(NRF54L_IRQ_TIMER10, MPSL_HIGH_IRQ_PRIORITY);
+ up_prioritize_irq(NRF54L_IRQ_RADIO_0, MPSL_HIGH_IRQ_PRIORITY);
+
+ up_enable_irq(NRF54L_IRQ_GRTC_3);
+ up_enable_irq(NRF54L_IRQ_TIMER10);
+ up_enable_irq(NRF54L_IRQ_RADIO_0);
+
+ /* Initialize MPSL */
+
+ ret = mpsl_init(NULL, NRF54L_IRQ_SWI03 - NRF54L_IRQ_EXTINT,
+ &mpsl_assert_handler);
+ if (ret < 0)
+ {
+ wlerr("mpsl_init failed: %d\n", ret);
+ goto errout_radio;
+ }
+
+ g_sdc_dev.mpsl_initialized = true;
+
+ /* Initialize SDC */
+
+ ret = sdc_init(&sdc_fault_handler);
+ if (ret < 0)
+ {
+ wlerr("sdc_init failed: %d\n", ret);
+ goto errout_mpsl;
+ }
+
+ /* Configure SoftDevice random sources */
+
+ ret = nrf54l_configure_rand();
+ if (ret < 0)
+ {
+ wlerr("nrf54l_configure_rand failed: %d\n", ret);
+ goto errout_mpsl;
+ }
+
+ /* Configure SoftDevice features */
+
+ ret = nrf54l_configure_features();
+ if (ret < 0)
+ {
+ wlerr("nrf54l_configure_features failed: %d\n", ret);
+ goto errout_mpsl;
+ }
+
+ /* Configure SoftDevice memory */
+
+ ret = nrf54l_configure_memory();
+ if (ret < 0)
+ {
+ wlerr("nrf54l_configure_memory failed: %d\n", ret);
+ goto errout_mpsl;
+ }
+
+ /* Finally enable SoftDevice Controller */
+
+ ret = sdc_enable(on_hci, g_sdc_dev.mempool);
+ if (ret < 0)
+ {
+ wlerr("sdc_enable failed: %d\n", ret);
+ goto errout_mem;
+ }
+
+#ifdef HAVE_BTADDR_CONFIGURE
+ ret = nrf54l_sdc_btaddr_configure();
+ if (ret < 0)
+ {
+ wlerr("Could not configure BT addr: %d\n", ret);
+ goto errout_sdc;
+ }
+#endif
+
+ /* Registration can start the host, which calls back into this driver. */
+
+ nxmutex_unlock(&g_sdc_dev.lock);
+ ret = bt_driver_register(&g_bt_driver);
+ nxmutex_lock(&g_sdc_dev.lock);
+ if (ret < 0)
+ {
+ wlerr("bt_driver_register error: %d\n", ret);
+ goto errout_sdc;
+ }
+
+ g_sdc_dev.initialized = true;
+ g_sdc_dev.initializing = false;
+ nxmutex_unlock(&g_sdc_dev.lock);
+ return OK;
+
+errout_sdc:
+ sdc_disable();
+ g_sdc_dev.opened = false;
+ work_cancel(LPWORK, &g_sdc_dev.work);
+ work_cancel(LPWORK, &g_sdc_dev.hci_work);
+
+errout_mem:
+ kmm_free(g_sdc_dev.mempool);
+ g_sdc_dev.mempool = NULL;
+
+errout_mpsl:
+ mpsl_uninit();
+ g_sdc_dev.mpsl_initialized = false;
+
+errout_radio:
+ up_disable_irq(NRF54L_IRQ_RADIO_0);
+ up_disable_irq(NRF54L_IRQ_TIMER10);
+ up_disable_irq(NRF54L_IRQ_GRTC_3);
+ arm_ramvec_attach(NRF54L_IRQ_RADIO_0, NULL);
+
+errout_timer:
+ arm_ramvec_attach(NRF54L_IRQ_TIMER10, NULL);
+
+errout_rtc:
+ arm_ramvec_attach(NRF54L_IRQ_GRTC_3, NULL);
+
+errout_power:
+ up_disable_irq(NRF54L_IRQ_CLOCK_POWER);
+ up_disable_irq(NRF54L_IRQ_SWI03);
+ irq_detach(NRF54L_IRQ_CLOCK_POWER);
+
+errout_swi:
+ irq_detach(NRF54L_IRQ_SWI03);
+
+errout_grtc:
+#ifndef CONFIG_NRF54L_SYSTIMER_GRTC
+ nrf54l_grtc_deinit(grtc);
+
+errout_clocks:
+#endif
+ mpsl_clock_ctrl_source_unregister();
+ g_sdc_dev.initializing = false;
+ nxmutex_unlock(&g_sdc_dev.lock);
+ return ret;
+}
diff --git a/arch/arm/src/nrf54l/nrf54l_sdc.h b/arch/arm/src/nrf54l/nrf54l_sdc.h
new file mode 100644
index 00000000000..1b72c7f470d
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_sdc.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_sdc.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_SDC_H
+#define __ARCH_ARM_SRC_NRF54L_NRF54L_SDC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_BLUETOOTH
+# define NRF54L_SDC_CENTRAL_COUNT (CONFIG_BLUETOOTH_MAX_CONN -
CONFIG_NRF54L_SDC_PERIPHERAL_COUNT)
+#else
+# define NRF54L_SDC_CENTRAL_COUNT (CONFIG_NRF54L_SDC_MAX_COUNT -
CONFIG_NRF54L_SDC_PERIPHERAL_COUNT)
+#endif
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+int nrf54l_sdc_initialize(void);
+uint8_t nrf54l_sdc_reset(void);
+size_t nrf54l_sdc_command(const uint8_t *command, uint8_t *event);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ARCH_ARM_SRC_NRF54L_NRF54L_SDC_H */
diff --git a/arch/arm/src/nrf54l/nrf54l_sdc_hci.c
b/arch/arm/src/nrf54l/nrf54l_sdc_hci.c
new file mode 100644
index 00000000000..87157e6974e
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_sdc_hci.c
@@ -0,0 +1,405 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_sdc_hci.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 <nuttx/debug.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+
+#include <sdc_hci_cmd_controller_baseband.h>
+#include <sdc_hci_cmd_info_params.h>
+#include <sdc_hci_cmd_le.h>
+#include <sdc_hci_cmd_link_control.h>
+#include <sdc_hci_cmd_status_params.h>
+
+#include "nrf54l_sdc.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nrf54l_sdc_cmd_s
+{
+ uint16_t opcode;
+ uint8_t params;
+ uint8_t response;
+ bool status;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* HCI wire lengths exclude the command header and return status byte.
+ * Commands completing asynchronously return Command Status instead.
+ */
+
+static const struct nrf54l_sdc_cmd_s g_commands[] =
+{
+ {0x0406, 3, 0, true}, /* Disconnect */
+ {0x041d, 2, 0, true}, /* Read Remote Version Information */
+ {0x0c01, 8, 0, false}, /* Set Event Mask */
+ {0x0c03, 0, 0, false}, /* Reset */
+ {0x0c2d, 3, 3, false}, /* Read Transmit Power Level */
+ {0x0c31, 1, 0, false}, /* Set Controller To Host Flow Control */
+ {0x0c33, 7, 0, false}, /* Host Buffer Size */
+ {0x0c35, 1, 0, false}, /* Host Number Of Completed Packets (variable) */
+ {0x0c63, 8, 0, false}, /* Set Event Mask Page 2 */
+ {0x1001, 0, 8, false}, /* Read Local Version Information */
+ {0x1002, 0, 64, false}, /* Read Local Supported Commands */
+ {0x1003, 0, 8, false}, /* Read Local Supported Features */
+ {0x1009, 0, 6, false}, /* Read BD_ADDR */
+ {0x1405, 2, 3, false}, /* Read RSSI */
+ {0x2001, 8, 0, false}, /* LE Set Event Mask */
+ {0x2002, 0, 3, false}, /* LE Read Buffer Size */
+ {0x2003, 0, 8, false}, /* LE Read Local Supported Features */
+ {0x2005, 6, 0, false}, /* LE Set Random Address */
+ {0x2006, 15, 0, false}, /* LE Set Advertising Parameters */
+ {0x2007, 0, 1, false}, /* LE Read Advertising Channel TX Power */
+ {0x2008, 32, 0, false}, /* LE Set Advertising Data */
+ {0x2009, 32, 0, false}, /* LE Set Scan Response Data */
+ {0x200a, 1, 0, false}, /* LE Set Advertising Enable */
+ {0x200b, 7, 0, false}, /* LE Set Scan Parameters */
+ {0x200c, 2, 0, false}, /* LE Set Scan Enable */
+ {0x200d, 25, 0, true}, /* LE Create Connection */
+ {0x200e, 0, 0, false}, /* LE Create Connection Cancel */
+ {0x200f, 0, 1, false}, /* LE Read Filter Accept List Size */
+ {0x2010, 0, 0, false}, /* LE Clear Filter Accept List */
+ {0x2011, 7, 0, false}, /* LE Add Device To Filter Accept List */
+ {0x2012, 7, 0, false}, /* LE Remove Device From Filter Accept List */
+ {0x2013, 14, 0, true}, /* LE Connection Update */
+ {0x2014, 5, 0, false}, /* LE Set Host Channel Classification */
+ {0x2015, 2, 7, false}, /* LE Read Channel Map */
+ {0x2016, 2, 0, true}, /* LE Read Remote Features */
+ {0x2017, 32, 16, false}, /* LE Encrypt */
+ {0x2018, 0, 8, false}, /* LE Rand */
+ {0x2019, 28, 0, true}, /* LE Enable Encryption */
+ {0x201a, 18, 2, false}, /* LE Long Term Key Request Reply */
+ {0x201b, 2, 2, false}, /* LE Long Term Key Request Negative Reply */
+ {0x201c, 0, 8, false}, /* LE Read Supported States */
+ {0x2022, 6, 2, false}, /* LE Set Data Length */
+ {0x2023, 0, 4, false}, /* LE Read Suggested Default Data Length */
+ {0x2024, 4, 0, false}, /* LE Write Suggested Default Data Length */
+ {0x202f, 0, 8, false}, /* LE Read Maximum Data Length */
+ {0x2030, 2, 4, false}, /* LE Read PHY */
+ {0x2031, 3, 0, false}, /* LE Set Default PHY */
+ {0x2032, 7, 0, true}, /* LE Set PHY */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_sdc_supported_commands
+ *
+ * Description:
+ * Report only commands implemented by this HCI transport.
+ *
+ ****************************************************************************/
+
+static void nrf54l_sdc_supported_commands(void *response)
+{
+ sdc_hci_ip_supported_commands_t *cmd = response;
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->hci_disconnect = 1;
+ cmd->hci_read_remote_version_information = 1;
+ cmd->hci_set_event_mask = 1;
+ cmd->hci_reset = 1;
+ cmd->hci_read_transmit_power_level = 1;
+ cmd->hci_set_controller_to_host_flow_control = 1;
+ cmd->hci_host_buffer_size = 1;
+ cmd->hci_host_number_of_completed_packets = 1;
+ cmd->hci_set_event_mask_page_2 = 1;
+ cmd->hci_read_local_version_information = 1;
+ cmd->hci_read_local_supported_features = 1;
+ cmd->hci_read_bd_addr = 1;
+ cmd->hci_read_rssi = 1;
+ cmd->hci_le_set_event_mask = 1;
+ cmd->hci_le_read_buffer_size_v1 = 1;
+ cmd->hci_le_read_local_supported_features = 1;
+ cmd->hci_le_set_random_address = 1;
+#ifdef CONFIG_NRF54L_SDC_ADVERTISING
+ cmd->hci_le_set_advertising_parameters = 1;
+ cmd->hci_le_read_advertising_physical_channel_tx_power = 1;
+ cmd->hci_le_set_advertising_data = 1;
+ cmd->hci_le_set_scan_response_data = 1;
+ cmd->hci_le_set_advertising_enable = 1;
+#endif
+#ifdef CONFIG_NRF54L_SDC_SCANNING
+ cmd->hci_le_set_scan_parameters = 1;
+ cmd->hci_le_set_scan_enable = 1;
+#endif
+#if NRF54L_SDC_CENTRAL_COUNT > 0
+ cmd->hci_le_create_connection = 1;
+ cmd->hci_le_create_connection_cancel = 1;
+ cmd->hci_le_connection_update = 1;
+ cmd->hci_le_set_host_channel_classification = 1;
+ cmd->hci_le_enable_encryption = 1;
+#endif
+ cmd->hci_le_read_filter_accept_list_size = 1;
+ cmd->hci_le_clear_filter_accept_list = 1;
+ cmd->hci_le_add_device_to_filter_accept_list = 1;
+ cmd->hci_le_remove_device_from_filter_accept_list = 1;
+ cmd->hci_le_read_channel_map = 1;
+ cmd->hci_le_read_remote_features = 1;
+ cmd->hci_le_encrypt = 1;
+ cmd->hci_le_rand = 1;
+#if CONFIG_NRF54L_SDC_PERIPHERAL_COUNT > 0
+ cmd->hci_le_long_term_key_request_reply = 1;
+ cmd->hci_le_long_term_key_request_negative_reply = 1;
+#endif
+ cmd->hci_le_read_supported_states = 1;
+#ifdef CONFIG_NRF54L_SDC_DLE
+ cmd->hci_le_set_data_length = 1;
+ cmd->hci_le_read_suggested_default_data_length = 1;
+ cmd->hci_le_write_suggested_default_data_length = 1;
+ cmd->hci_le_read_maximum_data_length = 1;
+#endif
+#if defined(CONFIG_NRF54L_SDC_LE_2M_PHY) || \
+ defined(CONFIG_NRF54L_SDC_LE_CODED_PHY)
+ cmd->hci_le_read_phy = 1;
+ cmd->hci_le_set_default_phy = 1;
+ cmd->hci_le_set_phy = 1;
+#endif
+}
+
+/****************************************************************************
+ * Name: nrf54l_sdc_dispatch
+ ****************************************************************************/
+
+static uint8_t nrf54l_sdc_dispatch(uint16_t opcode, const void *params,
+ void *response)
+{
+ switch (opcode)
+ {
+ case SDC_HCI_OPCODE_CMD_LC_DISCONNECT:
+ return sdc_hci_cmd_lc_disconnect(params);
+ case SDC_HCI_OPCODE_CMD_LC_READ_REMOTE_VERSION_INFORMATION:
+ return sdc_hci_cmd_lc_read_remote_version_information(params);
+ case SDC_HCI_OPCODE_CMD_CB_SET_EVENT_MASK:
+ return sdc_hci_cmd_cb_set_event_mask(params);
+ case SDC_HCI_OPCODE_CMD_CB_RESET:
+ return nrf54l_sdc_reset();
+ case SDC_HCI_OPCODE_CMD_CB_READ_TRANSMIT_POWER_LEVEL:
+ return sdc_hci_cmd_cb_read_transmit_power_level(params, response);
+ case SDC_HCI_OPCODE_CMD_CB_SET_CONTROLLER_TO_HOST_FLOW_CONTROL:
+ return sdc_hci_cmd_cb_set_controller_to_host_flow_control(params);
+ case SDC_HCI_OPCODE_CMD_CB_HOST_BUFFER_SIZE:
+ return sdc_hci_cmd_cb_host_buffer_size(params);
+ case SDC_HCI_OPCODE_CMD_CB_HOST_NUMBER_OF_COMPLETED_PACKETS:
+ return sdc_hci_cmd_cb_host_number_of_completed_packets(params);
+ case SDC_HCI_OPCODE_CMD_CB_SET_EVENT_MASK_PAGE_2:
+ return sdc_hci_cmd_cb_set_event_mask_page_2(params);
+ case SDC_HCI_OPCODE_CMD_IP_READ_LOCAL_VERSION_INFORMATION:
+ return sdc_hci_cmd_ip_read_local_version_information(response);
+ case SDC_HCI_OPCODE_CMD_IP_READ_LOCAL_SUPPORTED_COMMANDS:
+ nrf54l_sdc_supported_commands(response);
+ return 0;
+ case SDC_HCI_OPCODE_CMD_IP_READ_LOCAL_SUPPORTED_FEATURES:
+ return sdc_hci_cmd_ip_read_local_supported_features(response);
+ case SDC_HCI_OPCODE_CMD_IP_READ_BD_ADDR:
+ return sdc_hci_cmd_ip_read_bd_addr(response);
+ case SDC_HCI_OPCODE_CMD_SP_READ_RSSI:
+ return sdc_hci_cmd_sp_read_rssi(params, response);
+ case SDC_HCI_OPCODE_CMD_LE_SET_EVENT_MASK:
+ return sdc_hci_cmd_le_set_event_mask(params);
+ case SDC_HCI_OPCODE_CMD_LE_READ_BUFFER_SIZE:
+ return sdc_hci_cmd_le_read_buffer_size(response);
+ case SDC_HCI_OPCODE_CMD_LE_READ_LOCAL_SUPPORTED_FEATURES:
+ return sdc_hci_cmd_le_read_local_supported_features(response);
+ case SDC_HCI_OPCODE_CMD_LE_SET_RANDOM_ADDRESS:
+ return sdc_hci_cmd_le_set_random_address(params);
+ case SDC_HCI_OPCODE_CMD_LE_SET_ADV_PARAMS:
+ return sdc_hci_cmd_le_set_adv_params(params);
+ case SDC_HCI_OPCODE_CMD_LE_READ_ADV_PHYSICAL_CHANNEL_TX_POWER:
+ return sdc_hci_cmd_le_read_adv_physical_channel_tx_power(response);
+ case SDC_HCI_OPCODE_CMD_LE_SET_ADV_DATA:
+ return sdc_hci_cmd_le_set_adv_data(params);
+ case SDC_HCI_OPCODE_CMD_LE_SET_SCAN_RESPONSE_DATA:
+ return sdc_hci_cmd_le_set_scan_response_data(params);
+ case SDC_HCI_OPCODE_CMD_LE_SET_ADV_ENABLE:
+ return sdc_hci_cmd_le_set_adv_enable(params);
+ case SDC_HCI_OPCODE_CMD_LE_SET_SCAN_PARAMS:
+ return sdc_hci_cmd_le_set_scan_params(params);
+ case SDC_HCI_OPCODE_CMD_LE_SET_SCAN_ENABLE:
+ return sdc_hci_cmd_le_set_scan_enable(params);
+ case SDC_HCI_OPCODE_CMD_LE_CREATE_CONN:
+ return sdc_hci_cmd_le_create_conn(params);
+ case SDC_HCI_OPCODE_CMD_LE_CREATE_CONN_CANCEL:
+ return sdc_hci_cmd_le_create_conn_cancel();
+ case SDC_HCI_OPCODE_CMD_LE_READ_FILTER_ACCEPT_LIST_SIZE:
+ return sdc_hci_cmd_le_read_filter_accept_list_size(response);
+ case SDC_HCI_OPCODE_CMD_LE_CLEAR_FILTER_ACCEPT_LIST:
+ return sdc_hci_cmd_le_clear_filter_accept_list();
+ case SDC_HCI_OPCODE_CMD_LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST:
+ return sdc_hci_cmd_le_add_device_to_filter_accept_list(params);
+ case SDC_HCI_OPCODE_CMD_LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST:
+ return sdc_hci_cmd_le_remove_device_from_filter_accept_list(params);
+ case SDC_HCI_OPCODE_CMD_LE_CONN_UPDATE:
+ return sdc_hci_cmd_le_conn_update(params);
+ case SDC_HCI_OPCODE_CMD_LE_SET_HOST_CHANNEL_CLASSIFICATION:
+ return sdc_hci_cmd_le_set_host_channel_classification(params);
+ case SDC_HCI_OPCODE_CMD_LE_READ_CHANNEL_MAP:
+ return sdc_hci_cmd_le_read_channel_map(params, response);
+ case SDC_HCI_OPCODE_CMD_LE_READ_REMOTE_FEATURES:
+ return sdc_hci_cmd_le_read_remote_features(params);
+ case SDC_HCI_OPCODE_CMD_LE_ENCRYPT:
+ return sdc_hci_cmd_le_encrypt(params, response);
+ case SDC_HCI_OPCODE_CMD_LE_RAND:
+ return sdc_hci_cmd_le_rand(response);
+ case SDC_HCI_OPCODE_CMD_LE_ENABLE_ENCRYPTION:
+ return sdc_hci_cmd_le_enable_encryption(params);
+ case SDC_HCI_OPCODE_CMD_LE_LONG_TERM_KEY_REQUEST_REPLY:
+ return sdc_hci_cmd_le_long_term_key_request_reply(params, response);
+ case SDC_HCI_OPCODE_CMD_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY:
+ return sdc_hci_cmd_le_long_term_key_request_negative_reply(params,
+ response);
+ case SDC_HCI_OPCODE_CMD_LE_READ_SUPPORTED_STATES:
+
+ /* Advertise the enabled individual Link Layer states. */
+
+ memset(response, 0, 8);
+#ifdef CONFIG_NRF54L_SDC_ADVERTISING
+ *(uint8_t *)response |= 0x03;
+# if CONFIG_NRF54L_SDC_PERIPHERAL_COUNT > 0
+ *(uint8_t *)response |= 0x8c;
+# endif
+#endif
+#ifdef CONFIG_NRF54L_SDC_SCANNING
+ *(uint8_t *)response |= 0x30;
+#endif
+#if NRF54L_SDC_CENTRAL_COUNT > 0
+ *(uint8_t *)response |= 0x40;
+#endif
+ return 0;
+ case SDC_HCI_OPCODE_CMD_LE_SET_DATA_LENGTH:
+ return sdc_hci_cmd_le_set_data_length(params, response);
+ case SDC_HCI_OPCODE_CMD_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH:
+ return sdc_hci_cmd_le_read_suggested_default_data_length(response);
+ case SDC_HCI_OPCODE_CMD_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH:
+ return sdc_hci_cmd_le_write_suggested_default_data_length(params);
+ case SDC_HCI_OPCODE_CMD_LE_READ_MAX_DATA_LENGTH:
+ return sdc_hci_cmd_le_read_max_data_length(response);
+ case SDC_HCI_OPCODE_CMD_LE_READ_PHY:
+ return sdc_hci_cmd_le_read_phy(params, response);
+ case SDC_HCI_OPCODE_CMD_LE_SET_DEFAULT_PHY:
+ return sdc_hci_cmd_le_set_default_phy(params);
+ case SDC_HCI_OPCODE_CMD_LE_SET_PHY:
+ return sdc_hci_cmd_le_set_phy(params);
+ default:
+ return 0x01; /* Unknown HCI Command */
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_sdc_command
+ *
+ * Description:
+ * Execute a validated HCI command in the serialized SDC context. Return
+ * the length of its Command Complete/Status event, or zero for successful
+ * Host Number Of Completed Packets, which must not generate an event.
+ * The caller supplies an HCI event buffer of at least 70 bytes.
+ *
+ ****************************************************************************/
+
+size_t nrf54l_sdc_command(const uint8_t *command, uint8_t *event)
+{
+ const struct nrf54l_sdc_cmd_s *desc = NULL;
+ uint16_t opcode = command[0] | (command[1] << 8);
+ uint8_t status = 0x01;
+ unsigned int length;
+ unsigned int i;
+
+ memset(event, 0, 70);
+ for (i = 0; i < nitems(g_commands); i++)
+ {
+ if (g_commands[i].opcode == opcode)
+ {
+ desc = &g_commands[i];
+ break;
+ }
+ }
+
+ if (desc != NULL)
+ {
+ length = desc->params;
+ if (opcode == SDC_HCI_OPCODE_CMD_CB_HOST_NUMBER_OF_COMPLETED_PACKETS &&
+ command[2] >= 1)
+ {
+ length += command[3] * 4;
+ }
+
+ if (command[2] == length)
+ {
+ status = nrf54l_sdc_dispatch(opcode, &command[3], &event[6]);
+ }
+ else
+ {
+ status = 0x12; /* Invalid HCI Command Parameters */
+ }
+ }
+
+ if (opcode == SDC_HCI_OPCODE_CMD_CB_HOST_NUMBER_OF_COMPLETED_PACKETS &&
+ status == 0)
+ {
+ return 0;
+ }
+
+ if (status != 0)
+ {
+ wlwarn("HCI command 0x%04x failed: 0x%02x\n", opcode, status);
+ }
+
+ if (desc != NULL && desc->status)
+ {
+ event[0] = 0x0f; /* Command Status */
+ event[1] = 4;
+ event[2] = status;
+ event[3] = 1;
+ event[4] = command[0];
+ event[5] = command[1];
+ }
+ else
+ {
+ event[0] = 0x0e; /* Command Complete */
+ event[1] = 4 + (desc != NULL ? desc->response : 0);
+ event[2] = 1;
+ event[3] = command[0];
+ event[4] = command[1];
+ event[5] = status;
+ }
+
+ return event[1] + 2;
+}
diff --git a/arch/arm/src/nrf54l/sdc/hal/nrf_clock.h
b/arch/arm/src/nrf54l/sdc/hal/nrf_clock.h
new file mode 100644
index 00000000000..ed7d17c4bb3
--- /dev/null
+++ b/arch/arm/src/nrf54l/sdc/hal/nrf_clock.h
@@ -0,0 +1,35 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/sdc/hal/nrf_clock.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_SDC_HAL_NRF_CLOCK_H
+#define __ARCH_ARM_SRC_NRF54L_SDC_HAL_NRF_CLOCK_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Clock capabilities used by the MPSL public interface. */
+
+#define NRF_CLOCK_HAS_HFCLK24M 0
+#define NRF_CLOCK_HAS_XO_TUNE 1
+
+#endif /* __ARCH_ARM_SRC_NRF54L_SDC_HAL_NRF_CLOCK_H */
diff --git a/arch/arm/src/nrf54l/sdc/nrf.h b/arch/arm/src/nrf54l/sdc/nrf.h
new file mode 100644
index 00000000000..153816cc78c
--- /dev/null
+++ b/arch/arm/src/nrf54l/sdc/nrf.h
@@ -0,0 +1,32 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/sdc/nrf.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_SDC_NRF_H
+#define __ARCH_ARM_SRC_NRF54L_SDC_NRF_H
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+typedef int IRQn_Type;
+
+#endif /* __ARCH_ARM_SRC_NRF54L_SDC_NRF_H */
diff --git a/arch/arm/src/nrf54l/sdc/nrfx.h b/arch/arm/src/nrf54l/sdc/nrfx.h
new file mode 100644
index 00000000000..9762a74eb00
--- /dev/null
+++ b/arch/arm/src/nrf54l/sdc/nrfx.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/sdc/nrfx.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_SDC_NRFX_H
+#define __ARCH_ARM_SRC_NRF54L_SDC_NRFX_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "nrf.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define __PACKED __attribute__((packed))
+#define __ALIGN(n) __attribute__((aligned(n)))
+
+#endif /* __ARCH_ARM_SRC_NRF54L_SDC_NRFX_H */