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

Reply via email to