You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by bt...@apache.org on 2021/02/02 22:40:36 UTC

[incubator-nuttx] branch master updated: nRF52: add support for building SoftDevice BLE controller

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 45b392b  nRF52: add support for building SoftDevice BLE controller
45b392b is described below

commit 45b392be7e67a7d330678c20b716e403bd107cc4
Author: Matias N <ma...@protobits.dev>
AuthorDate: Sun Jan 24 14:19:59 2021 -0300

    nRF52: add support for building SoftDevice BLE controller
---
 arch/arm/src/nrf52/.gitignore                      |   4 +
 arch/arm/src/nrf52/Kconfig                         |  73 ++-
 arch/arm/src/nrf52/Make.defs                       |  57 ++
 arch/arm/src/nrf52/nrf52_ppi.h                     |  14 +-
 arch/arm/src/nrf52/nrf52_sdc.c                     | 600 +++++++++++++++++++++
 .../arm/src/nrf52/nrf52_sdc.h                      |  69 +--
 arch/arm/src/nrf52/sdc/README.md                   |   2 +
 arch/arm/src/nrf52/sdc/core_cm4.h                  |  41 ++
 arch/arm/src/nrf52/sdc/nrf.h                       |  84 +++
 .../arm/nrf52/nrf52832-mdk/configs/sdc/defconfig   |  81 +++
 boards/arm/nrf52/nrf52832-mdk/src/nrf52_bringup.c  |  21 +
 wireless/bluetooth/bt_buf.c                        |   2 +
 12 files changed, 1001 insertions(+), 47 deletions(-)

diff --git a/arch/arm/src/nrf52/.gitignore b/arch/arm/src/nrf52/.gitignore
new file mode 100644
index 0000000..8f9b697
--- /dev/null
+++ b/arch/arm/src/nrf52/.gitignore
@@ -0,0 +1,4 @@
+CMSIS_5*
+sdk-nrfxlib*
+nrfx*
+*.tar.gz
diff --git a/arch/arm/src/nrf52/Kconfig b/arch/arm/src/nrf52/Kconfig
index 0514244..f7c32b1 100644
--- a/arch/arm/src/nrf52/Kconfig
+++ b/arch/arm/src/nrf52/Kconfig
@@ -185,6 +185,7 @@ config NRF52_WDT
 config NRF52_RADIO
 	bool "RADIO"
 	depends on EXPERIMENTAL
+	depends on !NRF52_SOFTDEVICE_CONTROLLER
 	default n
 
 config NRF52_NFCT
@@ -198,6 +199,7 @@ config NRF52_SAADC
 config NRF52_TIMER0
 	bool "TIMER0"
 	select NRF52_TIMER
+	depends on !NRF52_SOFTDEVICE_CONTROLLER
 	default n
 
 config NRF52_TIMER1
@@ -248,6 +250,7 @@ config NRF52_PPI
 config NRF52_RTC0
 	bool "RTC0"
 	select NRF52_RTC
+	depends on !NRF52_SOFTDEVICE_CONTROLLER
 	default n
 
 config NRF52_RTC1
@@ -355,7 +358,8 @@ if NRF52_SYSTIMER_RTC
 
 config NRF52_SYSTIMER_RTC_INSTANCE
   int "RTC timer instance"
-  default 0
+  default 0 if !NRF52_SOFTDEVICE_CONTROLLER
+  default 1 if NRF52_SOFTDEVICE_CONTROLLER
   range 0 2
   ---help---
     Which RTC instance to use to drive the system timer
@@ -654,3 +658,70 @@ config NRF52_I2C_MASTER_COPY_BUF_SIZE
 		on malloc.
 
 endmenu
+
+menuconfig NRF52_SOFTDEVICE_CONTROLLER
+	bool "SoftDevice Controller"
+	select CONFIG_ARMV7M_USEBASEPRI
+	select CONFIG_ARCH_RAMVECTORS
+	select NRF52_USE_LFCLK
+	select NRF52_LFCLK_XTAL
+	---help---
+		This enables use of Nordic SoftDevice controller
+		(SDC). It is a library version of a subset of 
+		full SoftDevice, which only includes the BLE
+		controller implementation.
+
+		It makes use of RTC0, TIMER0, RADIO and RNG, so
+		these will be unavailable for direct use by user.
+		It also makes use of PPI channel range 17-31.
+
+if NRF52_SOFTDEVICE_CONTROLLER
+
+config NRF52_SDC_CLOCK_ACCURACY
+	int "Clock Accuracy [PPM]"
+	default 250
+	---help---
+		Select the clock accuracy depending on the chosen low-frequency clock
+		source
+
+config NRF52_SDC_SLAVE_COUNT
+	int "Number of slave roles to support (also master)"
+	default 1
+	---help---
+		This controls how many slave connections will be supported. It also
+		determines the number of master roles from the following:
+
+		MASTER_ROLES = CONFIG_BLUETOOTH_MAX_CONN - NRF52_SDC_SLAVE_COUNT
+
+		So by choosing these two variables you can control both capabilities.
+
+config NRF52_SDC_ADVERTISING
+	bool "Support advertising"
+	default y
+
+config NRF52_SDC_SCANNING
+	bool "Support scanning"
+	default y
+
+config NRF52_SDC_LE_2M_PHY
+	bool "Support LE 2M PHY"
+	default y
+
+config NRF52_SDC_LE_CODED_PHY
+	bool "Support LE Coded PHY"
+	default y
+
+config NRF52_SDC_DLE
+	bool "Support Data Length Extension (DLE)"
+	default y
+
+config NRF52_SDC_VERSION
+	int "SoftDevice version"
+	default 132 if ARCH_CHIP_NRF52832
+	default 140 if ARCH_CHIP_NRF52840
+	---help---
+		The softdevice version to use. This depends on the particular chip
+		to use. See official Nordic documentation on which chips are supported
+		in each version.
+endif
+
diff --git a/arch/arm/src/nrf52/Make.defs b/arch/arm/src/nrf52/Make.defs
index 9409876..de8eccb 100644
--- a/arch/arm/src/nrf52/Make.defs
+++ b/arch/arm/src/nrf52/Make.defs
@@ -171,3 +171,60 @@ endif
 ifeq ($(CONFIG_PM),y)
 CHIP_CSRCS += nrf52_pminitialize.c
 endif
+
+ifeq ($(CONFIG_NRF52_SOFTDEVICE_CONTROLLER),y)
+
+NRFXLIB_UNPACK  := sdk-nrfxlib
+NRFXLIB_VER     := 1.4.2
+NRFXLIB_REF     := v$(NRFXLIB_VER)
+NRFXLIB_TGZ     := $(NRFXLIB_REF).tar.gz
+NRFXLIB_URL     := https://github.com/nrfconnect/sdk-nrfxlib/archive
+
+$(NRFXLIB_TGZ):
+	$(Q) echo "Downloading: NRFXLIB"
+	$(Q) curl -L $(NRFXLIB_URL)/$(NRFXLIB_TGZ) -o chip/$(NRFXLIB_TGZ)
+
+chip/$(NRFXLIB_UNPACK): $(NRFXLIB_TGZ)
+	$(Q) echo "Unpacking: NRXFLIB"
+	$(Q) cd chip && tar zxf $(NRFXLIB_TGZ)
+	$(Q) mv chip/$(NRFXLIB_UNPACK)-$(NRFXLIB_VER)* chip/$(NRFXLIB_UNPACK)
+	$(Q) touch chip/$(NRFXLIB_UNPACK)
+
+context:: chip/$(NRFXLIB_UNPACK)
+
+clean_context::
+	$(call DELFILE, chip/$(NRFXLIB_TGZ))
+	$(call DELDIR, chip/$(NRFXLIB_UNPACK))
+
+CHIP_CSRCS += nrf52_sdc.c
+
+NRFX_DIR = $(ARCH_SRCDIR)$(DELIM)chip$(DELIM)nrfx
+NRFXLIB_DIR = $(ARCH_SRCDIR)$(DELIM)chip$(DELIM)sdk-nrfxlib
+CMSIS_DIR = $(ARCH_SRCDIR)$(DELIM)chip$(DELIM)CMSIS_5
+
+INCLUDES += \
+	$(shell $(INCDIR) "$(CC)" $(ARCH_SRCDIR)$(DELIM)chip$(DELIM)sdc) \
+	$(shell $(INCDIR) "$(CC)" $(NRFXLIB_DIR)$(DELIM)mpsl$(DELIM)include) \
+	$(shell $(INCDIR) "$(CC)" $(NRFXLIB_DIR)$(DELIM)softdevice_controller$(DELIM)include)
+
+ifeq ($(CONFIG_ARCH_CHIP_NRF52832),y)
+	CFLAGS += -DNRF52832_XXAB
+else
+	ifeq ($(CONFIG_ARCH_CHIP_NRF52840),y)
+		CFLAGS += -DNRF52840_XXAB
+	endif
+endif
+
+ifeq ($(CONFIG_ARCH_FPU),y)
+	LIB_VARIANT=hard-float
+else
+	LIB_VARIANT=soft-float
+endif
+
+EXTRA_LIBPATHS += \
+	-L $(NRFXLIB_DIR)$(DELIM)mpsl$(DELIM)lib$(DELIM)cortex-m4$(DELIM)$(LIB_VARIANT) \
+	-L $(NRFXLIB_DIR)$(DELIM)softdevice_controller$(DELIM)lib$(DELIM)cortex-m4$(DELIM)$(LIB_VARIANT)
+
+EXTRA_LIBS += -lmpsl -lsoftdevice_controller_s$(CONFIG_NRF52_SDC_VERSION)
+
+endif
diff --git a/arch/arm/src/nrf52/nrf52_ppi.h b/arch/arm/src/nrf52/nrf52_ppi.h
index 9f7b283..f7b82b8 100644
--- a/arch/arm/src/nrf52/nrf52_ppi.h
+++ b/arch/arm/src/nrf52/nrf52_ppi.h
@@ -32,9 +32,17 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
-#define NRF52_PPI_NUM_CHANNELS              32    /* Total number of PPI channels */
-#define NRF52_PPI_NUM_CONFIGURABLE_CHANNELS 20    /* Number of configurable PPI channels */
-#define NRF52_PPI_NUM_GROUPS                6     /* Number of PPI channel groups */
+/* Nordic SDC makes use of PPI channels 17-31 */
+
+#ifdef CONFIG_NRF52_SOFTDEVICE_CONTROLLER
+#  define NRF52_PPI_NUM_CHANNELS              16    /* Total number of PPI channels */
+#  define NRF52_PPI_NUM_CONFIGURABLE_CHANNELS 16    /* Number of configurable PPI channels */
+#  define NRF52_PPI_NUM_GROUPS                6     /* Number of PPI channel groups */
+#else
+#  define NRF52_PPI_NUM_CHANNELS              32    /* Total number of PPI channels */
+#  define NRF52_PPI_NUM_CONFIGURABLE_CHANNELS 20    /* Number of configurable PPI channels */
+#  define NRF52_PPI_NUM_GROUPS                6     /* Number of PPI channel groups */
+#endif
 
 /****************************************************************************
  * Public Types
diff --git a/arch/arm/src/nrf52/nrf52_sdc.c b/arch/arm/src/nrf52/nrf52_sdc.c
new file mode 100644
index 0000000..8c23213
--- /dev/null
+++ b/arch/arm/src/nrf52/nrf52_sdc.c
@@ -0,0 +1,600 @@
+/****************************************************************************
+ * arch/arm/src/chip/nrf52_sdc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.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 <arch/armv7-m/nvicpri.h>
+#include <arch/nrf52/nrf52_irq.h>
+#include <nuttx/wqueue.h>
+
+#include "arm_internal.h"
+#include "ram_vectors.h"
+#include "arm_arch.h"
+
+#include <mpsl.h>
+#include <sdc.h>
+#include <sdc_hci.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#if defined(CONFIG_SDC_SLAVE_COUNT) && \
+    CONFIG_SDC_SLAVE_COUNT > CONFIG_BLUETOOTH_MAX_CONN
+#  error "Cannot support more BLE slave roles than connections"
+#endif
+
+#define SDC_MASTER_COUNT (CONFIG_BLUETOOTH_MAX_CONN - \
+                          CONFIG_NRF52_SDC_SLAVE_COUNT)
+
+/* Todo: check central/peripheral against master/slave count */
+
+#define MASTER_MEM_SIZE  (SDC_MEM_PER_MASTER_LINK( \
+                          SDC_DEFAULT_TX_PACKET_SIZE, \
+                          SDC_DEFAULT_RX_PACKET_SIZE, \
+                          SDC_DEFAULT_TX_PACKET_COUNT, \
+                          SDC_DEFAULT_RX_PACKET_COUNT) \
+                          + SDC_MEM_MASTER_LINKS_SHARED)
+
+#define SLAVE_MEM_SIZE (SDC_MEM_PER_SLAVE_LINK( \
+                        SDC_DEFAULT_TX_PACKET_SIZE, \
+                        SDC_DEFAULT_RX_PACKET_SIZE, \
+                        SDC_DEFAULT_TX_PACKET_COUNT, \
+                        SDC_DEFAULT_RX_PACKET_COUNT) \
+                        + SDC_MEM_SLAVE_LINKS_SHARED)
+
+#define MEMPOOL_SIZE  ((CONFIG_NRF52_SDC_SLAVE_COUNT * SLAVE_MEM_SIZE) + \
+                       (SDC_MASTER_COUNT * MASTER_MEM_SIZE))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nrf52_sdc_dev_s
+{
+  uint8_t mempool[MEMPOOL_SIZE];
+  uint8_t msg_buffer[HCI_MSG_BUFFER_MAX_SIZE];
+
+  sem_t exclsem;
+  struct work_s work;
+};
+
+/****************************************************************************
+ * 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(FAR const struct bt_driver_s *btdev);
+static int bt_hci_send(FAR const struct bt_driver_s *btdev,
+                       FAR struct bt_buf_s *buf);
+
+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, FAR void *context, FAR void *arg);
+static int power_clock_isr(int irq, FAR void *context, FAR void *arg);
+
+static void rng_handler(void);
+static void rtc0_handler(void);
+static void timer0_handler(void);
+static void radio_handler(void);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct bt_driver_s g_bt_driver =
+{
+  .head_reserve = 0,
+  .open         = bt_open,
+  .send         = bt_hci_send
+};
+
+static const mpsl_clock_lfclk_cfg_t g_clock_config =
+{
+  .source                   = MPSL_CLOCK_LF_SRC_XTAL,
+  .rc_ctiv                  = 0,
+  .rc_temp_ctiv             = 0,
+  .accuracy_ppm             = CONFIG_NRF52_SDC_CLOCK_ACCURACY,
+  .skip_wait_lfclk_started  = false
+};
+
+static struct nrf52_sdc_dev_s g_sdc_dev;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bt_open
+ ****************************************************************************/
+
+static int bt_open(FAR const struct bt_driver_s *btdev)
+{
+  return 0;
+}
+
+/****************************************************************************
+ * Name: bt_open
+ ****************************************************************************/
+
+static int bt_hci_send(FAR const struct bt_driver_s *btdev,
+                       FAR struct bt_buf_s *buf)
+{
+  int ret = OK;
+
+  /* Pass HCI CMD/DATA to SDC */
+
+  if (buf->type == BT_CMD)
+    {
+      struct bt_hci_cmd_hdr_s *cmd = (struct bt_hci_cmd_hdr_s *)buf->data;
+
+      wlinfo("passing CMD %d to softdevice\n", cmd->opcode);
+
+      /* Ensure non-concurrent access to SDC operations */
+
+      nxsem_wait_uninterruptible(&g_sdc_dev.exclsem);
+
+      if (sdc_hci_cmd_put(buf->data) < 0)
+        {
+          wlerr("sdc_hci_cmd_put() failed\n");
+          ret = -EIO;
+        }
+
+      nxsem_post(&g_sdc_dev.exclsem);
+
+      work_queue(LPWORK, &g_sdc_dev.work, on_hci_worker, NULL, 0);
+    }
+  else if (buf->type == BT_ACL_OUT)
+    {
+      wlinfo("passing ACL to softdevice\n");
+
+      /* Ensure non-concurrent access to SDC operations */
+
+      nxsem_wait_uninterruptible(&g_sdc_dev.exclsem);
+
+      if (sdc_hci_data_put(buf->data) < 0)
+        {
+          wlerr("sdc_hci_data_put() failed\n");
+          ret = -EIO;
+        }
+
+      nxsem_post(&g_sdc_dev.exclsem);
+
+      work_queue(LPWORK, &g_sdc_dev.work, on_hci_worker, NULL, 0);
+    }
+
+  if (ret < 0)
+    {
+      wlerr("bt_hci_send() failed: %d\n", ret);
+      return ret;
+    }
+  else
+    {
+      return buf->len;
+    }
+}
+
+/****************************************************************************
+ * Name: sdc_fault_handler
+ ****************************************************************************/
+
+static void sdc_fault_handler(const char *file, const uint32_t line)
+{
+  _alert("SoftDevice Controller Fault\n");
+  up_assert(file, line);
+}
+
+/****************************************************************************
+ * Name: mpsl_assert_handler
+ ****************************************************************************/
+
+static void mpsl_assert_handler(const char *const file, const uint32_t line)
+{
+  _alert("MPSL assertion failed\n");
+  up_assert(file, line);
+}
+
+/****************************************************************************
+ * 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.
+   */
+
+  nxsem_wait_uninterruptible(&g_sdc_dev.exclsem);
+  mpsl_low_priority_process();
+  nxsem_post(&g_sdc_dev.exclsem);
+}
+
+/****************************************************************************
+ * Name: on_hci_worker
+ ****************************************************************************/
+
+static void on_hci_worker(void *arg)
+{
+  /* We use this worker to force a call to on_hci() right after sending
+   * an HCI command as MPSL/SDC does not always signal the low priority
+   * worker
+   */
+
+  nxsem_wait_uninterruptible(&g_sdc_dev.exclsem);
+  on_hci();
+  nxsem_post(&g_sdc_dev.exclsem);
+}
+
+/****************************************************************************
+ * Name: on_hci
+ ****************************************************************************/
+
+static void on_hci(void)
+{
+  struct bt_buf_s *outbuf;
+  size_t len;
+  int ret;
+  bool check_again = true;
+
+  while (check_again)
+    {
+      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
+       */
+
+      ret = sdc_hci_evt_get(g_sdc_dev.msg_buffer);
+
+      if (ret == 0)
+        {
+          struct bt_hci_evt_hdr_s *hdr =
+              (struct bt_hci_evt_hdr_s *)g_sdc_dev.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 *)
+                      (g_sdc_dev.msg_buffer + sizeof(*hdr));
+              uint8_t *status = (uint8_t *)cmd_complete + 1;
+
+              wlinfo("received CMD_COMPLETE from softdevice "
+                     "(opcode: %d, status: 0x%x)\n",
+                     cmd_complete->opcode, *status);
+            }
+          else
+            {
+              wlinfo("received HCI EVT from softdevice "
+                     "(evt: %d, len: %zu)\n", hdr->evt, len);
+            }
+#endif
+
+          outbuf = bt_buf_alloc(BT_EVT, NULL, BLUETOOTH_H4_HDRLEN);
+          bt_buf_extend(outbuf, len);
+
+          memcpy(outbuf->data, g_sdc_dev.msg_buffer, len);
+
+          bt_hci_receive(outbuf);
+
+          check_again = true;
+        }
+
+      /* Same for ACL */
+
+      ret = sdc_hci_data_get(g_sdc_dev.msg_buffer);
+
+      if (ret == 0)
+        {
+          struct bt_hci_acl_hdr_s *hdr =
+              (struct bt_hci_acl_hdr_s *)g_sdc_dev.msg_buffer;
+
+          wlinfo("received HCI ACL from softdevice (handle: %d)\n",
+                 hdr->handle);
+
+          len = sizeof(*hdr) + hdr->len;
+
+          outbuf = bt_buf_alloc(BT_ACL_IN, NULL, BLUETOOTH_H4_HDRLEN);
+          bt_buf_extend(outbuf, len);
+
+          memcpy(outbuf->data, g_sdc_dev.msg_buffer, len);
+
+          bt_hci_receive(outbuf);
+
+          check_again = true;
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: swi_isr
+ ****************************************************************************/
+
+static int swi_isr(int irq, FAR void *context, FAR void *arg)
+{
+  work_queue(LPWORK, &g_sdc_dev.work, low_prio_worker, NULL, 0);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: rng_handler
+ ****************************************************************************/
+
+static void rng_handler(void)
+{
+  sdc_RNG_IRQHandler();
+}
+
+/****************************************************************************
+ * Name: power_clock_isr
+ ****************************************************************************/
+
+static int power_clock_isr(int irq, FAR void *context, FAR 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();
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int nrf52_sdc_initialize(void)
+{
+  int ret;
+  int32_t required_memory;
+  sdc_cfg_t cfg;
+
+  /* Initialize device data */
+
+  memset(&g_sdc_dev, 0, sizeof(g_sdc_dev));
+  nxsem_init(&g_sdc_dev.exclsem, 0, 1);
+
+  /* Register interrupt handler for normal-priority events. SWI5 will be
+   * used by MPSL to delegate low-priority work
+   */
+
+  irq_attach(NRF52_IRQ_SWI5_EGU5, swi_isr, NULL);
+  irq_attach(NRF52_IRQ_POWER_CLOCK, power_clock_isr, NULL);
+
+  up_enable_irq(NRF52_IRQ_SWI5_EGU5);
+  up_enable_irq(NRF52_IRQ_POWER_CLOCK);
+
+  up_prioritize_irq(NRF52_IRQ_SWI5_EGU5, NVIC_SYSH_PRIORITY_DEFAULT);
+  up_prioritize_irq(NRF52_IRQ_POWER_CLOCK, NVIC_SYSH_PRIORITY_DEFAULT);
+
+  /* Use zero-latency interrupt for RNG as we're expected to not add any
+   * processing to the ISR
+   */
+
+  arm_ramvec_attach(NRF52_IRQ_RNG, rng_handler);
+  up_prioritize_irq(NRF52_IRQ_RNG, NVIC_SYSH_MAXNORMAL_PRIORITY);
+  up_enable_irq(NRF52_IRQ_RNG);
+
+  /* Register high-priority interrupts for specific peripherals */
+
+  arm_ramvec_attach(NRF52_IRQ_RTC0, rtc0_handler);
+  arm_ramvec_attach(NRF52_IRQ_TIMER0, timer0_handler);
+  arm_ramvec_attach(NRF52_IRQ_RADIO, radio_handler);
+
+  up_prioritize_irq(NRF52_IRQ_RTC0, MPSL_HIGH_IRQ_PRIORITY);
+  up_prioritize_irq(NRF52_IRQ_TIMER0, MPSL_HIGH_IRQ_PRIORITY);
+  up_prioritize_irq(NRF52_IRQ_RADIO, MPSL_HIGH_IRQ_PRIORITY);
+
+  up_enable_irq(NRF52_IRQ_RTC0);
+  up_enable_irq(NRF52_IRQ_TIMER0);
+  up_enable_irq(NRF52_IRQ_RADIO);
+
+  /* TODO: how do WFI again after high priority interrupt wakes MCU up? */
+
+  /* Initialize MPSL */
+
+  ret = mpsl_init(&g_clock_config, NRF52_IRQ_SWI5_EGU5 - NRF52_IRQ_EXTINT,
+                  &mpsl_assert_handler);
+
+  if (ret < 0)
+    {
+      wlerr("mpsl init failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Initialize SDC */
+
+  ret = sdc_init(&sdc_fault_handler);
+
+  if (ret < 0)
+    {
+      wlerr("mpsl init failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Set some parameters */
+
+  cfg.master_count.count = SDC_MASTER_COUNT;
+  ret = sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
+                    SDC_CFG_TYPE_MASTER_COUNT, &cfg);
+
+  if (ret < 0)
+    {
+      wlerr("Failed to set master role count: %d\n", ret);
+      return ret;
+    }
+
+  cfg.slave_count.count = CONFIG_NRF52_SDC_SLAVE_COUNT;
+  ret = sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
+                    SDC_CFG_TYPE_SLAVE_COUNT, &cfg);
+
+  if (ret < 0)
+    {
+      wlerr("Failed to set slave role count: %d\n", ret);
+      return ret;
+    }
+
+  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);
+      return ret;
+    }
+
+  /* Verify we have enough memory for our configuration */
+
+  ASSERT(required_memory <= sizeof(g_sdc_dev.mempool));
+
+  /* Turn on specific features */
+
+#ifdef CONFIG_NRF52_SDC_ADVERTISING
+  ret = sdc_support_adv();
+
+  if (ret < 0)
+    {
+      wlerr("Could not enable advertising feature: %d\n", ret);
+      return ret;
+    }
+#endif
+
+#ifdef CONFIG_NRF52_SDC_SCANNING
+  ret = sdc_support_scan();
+
+  if (ret < 0)
+    {
+      wlerr("Could not enable scanning feature: %d\n", ret);
+      return ret;
+    }
+#endif
+
+#if SDC_MASTER_COUNT > 0
+  ret = sdc_support_master();
+
+  if (ret < 0)
+    {
+      wlerr("Could not enable master feature: %d\n", ret);
+      return ret;
+    }
+#endif
+
+#if CONFIG_NRF52_SDC_SLAVE_COUNT > 0
+  ret = sdc_support_slave();
+
+  if (ret < 0)
+    {
+      wlerr("Could not enable slave feature: %d\n", ret);
+      return ret;
+    }
+#endif
+
+#ifdef CONFIG_NRF52_SDC_DLE
+  ret = sdc_support_dle();
+
+  if (ret < 0)
+    {
+      wlerr("Could not enable DLE feature: %d\n", ret);
+      return ret;
+    }
+#endif
+
+#ifdef CONFIG_NRF52_SDC_LE_2M_PHY
+  ret = sdc_support_le_2m_phy();
+
+  if (ret < 0)
+    {
+      wlerr("Could not enable 2M PHY feature: %d\n", ret);
+      return ret;
+    }
+#endif
+
+#ifdef CONFIG_NRF52_SDC_LE_CODED_PHY
+  ret = sdc_support_le_coded_phy();
+
+  if (ret < 0)
+    {
+      wlerr("Could not enable Coded PHY feature: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Finally enable SoftDevice Controller */
+
+  ret = sdc_enable(on_hci, g_sdc_dev.mempool);
+
+  if (ret < 0)
+    {
+      wlerr("SoftDevice controller enable failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Register network device */
+
+  ret = bt_netdev_register(&g_bt_driver);
+
+  return ret;
+}
diff --git a/boards/arm/nrf52/nrf52832-mdk/src/nrf52_bringup.c b/arch/arm/src/nrf52/nrf52_sdc.h
similarity index 59%
copy from boards/arm/nrf52/nrf52832-mdk/src/nrf52_bringup.c
copy to arch/arm/src/nrf52/nrf52_sdc.h
index 280824f..3399435 100644
--- a/boards/arm/nrf52/nrf52832-mdk/src/nrf52_bringup.c
+++ b/arch/arm/src/nrf52/nrf52_sdc.h
@@ -1,5 +1,5 @@
 /****************************************************************************
- * boards/arm/nrf52/nrf52832-mdk/src/nrf52_bringup.c
+ * arch/arm/src/chip/nrf52_sdc.h
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -18,65 +18,48 @@
  *
  ****************************************************************************/
 
+#ifndef __ARCH_ARM_SRC_NRF52_NRF52_SDC_H
+#define __ARCH_ARM_SRC_NRF52_NRF52_SDC_H
+
 /****************************************************************************
  * Included Files
  ****************************************************************************/
 
 #include <nuttx/config.h>
 
-#include <sys/types.h>
-#include <syslog.h>
-
-#ifdef CONFIG_NRF52_WDT
-#  include "nrf52_wdt_lowerhalf.h"
-#endif
-
-#ifdef CONFIG_USERLED
-#  include <nuttx/leds/userled.h>
-#endif
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
 
 /****************************************************************************
- * Public Functions
+ * Public Types
  ****************************************************************************/
 
 /****************************************************************************
- * Name: nrf52_bringup
- *
- * Description:
- *   Perform architecture-specific initialization
- *
- *   CONFIG_BOARD_LATE_INITIALIZE=y :
- *     Called from board_late_initialize().
- *
- *   CONFIG_BOARD_LATE_INITIALIZE=n && CONFIG_LIB_BOARDCTL=y :
- *     Called from the NSH library
- *
+ * Public Data
  ****************************************************************************/
 
-int nrf52_bringup(void)
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
 {
-  int ret;
+#else
+#define EXTERN extern
+#endif
 
-#ifdef CONFIG_NRF52_WDT
-  /* Start Watchdog timer */
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
 
-  ret = nrf52_wdt_initialize(CONFIG_WATCHDOG_DEVPATH, 1, 1);
-  if (ret < 0)
-    {
-      syslog(LOG_ERR, "ERROR: nrf52_wdt_initialize failed: %d\n", ret);
-    }
-#endif
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
 
-#ifdef CONFIG_USERLED
-  /* Register the LED driver */
+int nrf52_sdc_initialize(void);
 
-  ret = userled_lower_initialize(CONFIG_EXAMPLES_LEDS_DEVPATH);
-  if (ret < 0)
-    {
-      syslog(LOG_ERR, "ERROR: userled_lower_initialize() failed: %d\n", ret);
-    }
+#undef EXTERN
+#ifdef __cplusplus
+}
 #endif
 
-  UNUSED(ret);
-  return OK;
-}
+#endif /* __ARCH_ARM_SRC_NRF52_NRF52_SDC_H */
diff --git a/arch/arm/src/nrf52/sdc/README.md b/arch/arm/src/nrf52/sdc/README.md
new file mode 100644
index 0000000..0ab5f25
--- /dev/null
+++ b/arch/arm/src/nrf52/sdc/README.md
@@ -0,0 +1,2 @@
+This directory holds stub files for building Nordic SoftDevice Controller without having to pull
+NRFX and CMSIS just to have a few definitions.
diff --git a/arch/arm/src/nrf52/sdc/core_cm4.h b/arch/arm/src/nrf52/sdc/core_cm4.h
new file mode 100644
index 0000000..2c925c6
--- /dev/null
+++ b/arch/arm/src/nrf52/sdc/core_cm4.h
@@ -0,0 +1,41 @@
+/****************************************************************************
+ * arch/arm/src/chip/nrf52_sdc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_NRF52_SDC_CORE_CM4_H__
+#define __ARCH_ARM_SRC_NRF52_SDC_CORE_CM4_H__
+
+/****************************************************************************
+ * Preprocessor Definitions
+ ****************************************************************************/
+
+/* These are the definitions needed from CMSIS */
+
+#ifdef __cplusplus
+#  define   __I     volatile
+#else
+#  define   __I     volatile const
+#endif
+#define     __O     volatile
+#define     __IO    volatile
+#define     __IM    volatile const
+#define     __OM    volatile
+#define     __IOM   volatile
+
+#endif /* __ARCH_ARM_SRC_NRF52_SDC_CORE_CM4_H__ */
diff --git a/arch/arm/src/nrf52/sdc/nrf.h b/arch/arm/src/nrf52/sdc/nrf.h
new file mode 100644
index 0000000..46eb32f
--- /dev/null
+++ b/arch/arm/src/nrf52/sdc/nrf.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+ * arch/arm/src/chip/nrf52_sdc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_NRF52_SDC_NRF_H__
+#define __ARCH_ARM_SRC_NRF52_SDC_NRF_H__
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This is the only definition we need from NRFX. We could simply do a
+ * typedef to an integer but we cannot ensure it will end up being the right
+ * size, so we replicate all values here.
+ */
+
+typedef enum
+{
+  Reset_IRQn                             = -15,
+  NonMaskableInt_IRQn                    = -14,
+  HardFault_IRQn                         = -13,
+  MemoryManagement_IRQn                  = -12,
+  BusFault_IRQn                          = -11,
+  UsageFault_IRQn                        = -10,
+  SVCall_IRQn                            =  -5,
+  DebugMonitor_IRQn                      =  -4,
+  PendSV_IRQn                            =  -2,
+  SysTick_IRQn                           =  -1,
+  POWER_CLOCK_IRQn                       =   0,
+  RADIO_IRQn                             =   1,
+  UARTE0_UART0_IRQn                      =   2,
+  SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn =   3,
+  SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn =   4,
+  NFCT_IRQn                              =   5,
+  GPIOTE_IRQn                            =   6,
+  SAADC_IRQn                             =   7,
+  TIMER0_IRQn                            =   8,
+  TIMER1_IRQn                            =   9,
+  TIMER2_IRQn                            =  10,
+  RTC0_IRQn                              =  11,
+  TEMP_IRQn                              =  12,
+  RNG_IRQn                               =  13,
+  ECB_IRQn                               =  14,
+  CCM_AAR_IRQn                           =  15,
+  WDT_IRQn                               =  16,
+  RTC1_IRQn                              =  17,
+  QDEC_IRQn                              =  18,
+  COMP_LPCOMP_IRQn                       =  19,
+  SWI0_EGU0_IRQn                         =  20,
+  SWI1_EGU1_IRQn                         =  21,
+  SWI2_EGU2_IRQn                         =  22,
+  SWI3_EGU3_IRQn                         =  23,
+  SWI4_EGU4_IRQn                         =  24,
+  SWI5_EGU5_IRQn                         =  25,
+  TIMER3_IRQn                            =  26,
+  TIMER4_IRQn                            =  27,
+  PWM0_IRQn                              =  28,
+  PDM_IRQn                               =  29,
+  MWU_IRQn                               =  32,
+  PWM1_IRQn                              =  33,
+  PWM2_IRQn                              =  34,
+  SPIM2_SPIS2_SPI2_IRQn                  =  35,
+  RTC2_IRQn                              =  36,
+  I2S_IRQn                               =  37,
+  FPU_IRQn                               =  38
+} IRQn_Type;
+
+#endif /* __ARCH_ARM_SRC_NRF52_SDC_NRF_H__ */
diff --git a/boards/arm/nrf52/nrf52832-mdk/configs/sdc/defconfig b/boards/arm/nrf52/nrf52832-mdk/configs/sdc/defconfig
new file mode 100644
index 0000000..77ec310
--- /dev/null
+++ b/boards/arm/nrf52/nrf52832-mdk/configs/sdc/defconfig
@@ -0,0 +1,81 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_NET_ETHERNET is not set
+# CONFIG_NET_IPv4 is not set
+# CONFIG_NRF52_SDC_DLE is not set
+# CONFIG_NRF52_SDC_LE_2M_PHY is not set
+# CONFIG_NRF52_SDC_LE_CODED_PHY is not set
+# CONFIG_WIRELESS_BLUETOOTH_HOST is not set
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="nrf52832-mdk"
+CONFIG_ARCH_BOARD_NRF52832_MDK=y
+CONFIG_ARCH_CHIP="nrf52"
+CONFIG_ARCH_CHIP_NRF52832=y
+CONFIG_ARCH_CHIP_NRF52=y
+CONFIG_ARCH_HIPRI_INTERRUPT=y
+CONFIG_ARCH_RAMVECTORS=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_ARCH_STDARG_H=y
+CONFIG_ARMV7M_SYSTICK=y
+CONFIG_BLUETOOTH_TXCMD_NMSGS=8
+CONFIG_BLUETOOTH_TXCMD_STACKSIZE=1024
+CONFIG_BLUETOOTH_TXCONN_NMSGS=8
+CONFIG_BLUETOOTH_TXCONN_STACKSIZE=1024
+CONFIG_BOARD_LOOPSPERMSEC=5500
+CONFIG_BUILTIN=y
+CONFIG_CLOCK_MONOTONIC=y
+CONFIG_FAT_LCNAMES=y
+CONFIG_FAT_LFN=y
+CONFIG_FS_FAT=y
+CONFIG_FS_PROCFS=y
+CONFIG_IDLETHREAD_STACKSIZE=2048
+CONFIG_MAX_TASKS=16
+CONFIG_MM_REGIONS=2
+CONFIG_NET=y
+CONFIG_NETDEV_LATEINIT=y
+CONFIG_NET_BLUETOOTH=y
+CONFIG_NET_BLUETOOTH_BACKLOG=0
+CONFIG_NFILE_DESCRIPTORS=8
+CONFIG_NIMBLE=y
+CONFIG_NIMBLE_REF="5fbb6660a433217b8eaf1b95de410379f868616c"
+CONFIG_NRF52_DCDC=y
+CONFIG_NRF52_HFCLK_XTAL=y
+CONFIG_NRF52_RTC1=y
+CONFIG_NRF52_SOFTDEVICE_CONTROLLER=y
+CONFIG_NRF52_SYSTIMER_RTC=y
+CONFIG_NRF52_UART0=y
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_LINELEN=64
+CONFIG_NSH_READLINE=y
+CONFIG_PREALLOC_MQ_MSGS=16
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_PTHREAD_MUTEX_TYPES=y
+CONFIG_RAM_SIZE=65535
+CONFIG_RAM_START=0x20000000
+CONFIG_RAW_BINARY=y
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP=y
+CONFIG_SCHED_WAITPID=y
+CONFIG_SDCLONE_DISABLE=y
+CONFIG_SIG_DEFAULT=y
+CONFIG_SIG_EVTHREAD=y
+CONFIG_SIG_SIGALRM_ACTION=y
+CONFIG_SIG_SIGUSR1_ACTION=y
+CONFIG_SIG_SIGUSR2_ACTION=y
+CONFIG_START_DAY=26
+CONFIG_START_MONTH=3
+CONFIG_SYMTAB_ORDEREDBYNAME=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TIMER=y
+CONFIG_UART0_SERIAL_CONSOLE=y
+CONFIG_USEC_PER_TICK=10000
+CONFIG_USER_ENTRYPOINT="nsh_main"
+CONFIG_WIRELESS=y
+CONFIG_WIRELESS_BLUETOOTH=y
diff --git a/boards/arm/nrf52/nrf52832-mdk/src/nrf52_bringup.c b/boards/arm/nrf52/nrf52832-mdk/src/nrf52_bringup.c
index 280824f..1421ebb 100644
--- a/boards/arm/nrf52/nrf52832-mdk/src/nrf52_bringup.c
+++ b/boards/arm/nrf52/nrf52832-mdk/src/nrf52_bringup.c
@@ -27,6 +27,10 @@
 #include <sys/types.h>
 #include <syslog.h>
 
+#ifdef CONFIG_FS_PROCFS
+#include <sys/mount.h>
+#endif
+
 #ifdef CONFIG_NRF52_WDT
 #  include "nrf52_wdt_lowerhalf.h"
 #endif
@@ -35,6 +39,10 @@
 #  include <nuttx/leds/userled.h>
 #endif
 
+#ifdef CONFIG_NRF52_SOFTDEVICE_CONTROLLER
+#include "nrf52_sdc.h"
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -57,6 +65,10 @@ int nrf52_bringup(void)
 {
   int ret;
 
+#ifdef CONFIG_FS_PROCFS
+  mount(NULL, "/proc", "procfs", 0, NULL);
+#endif
+
 #ifdef CONFIG_NRF52_WDT
   /* Start Watchdog timer */
 
@@ -77,6 +89,15 @@ int nrf52_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_NRF52_SOFTDEVICE_CONTROLLER
+  ret = nrf52_sdc_initialize();
+
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: nrf52_sdc_initialize() failed: %d\n", ret);
+    }
+#endif
+
   UNUSED(ret);
   return OK;
 }
diff --git a/wireless/bluetooth/bt_buf.c b/wireless/bluetooth/bt_buf.c
index f39b2d9..26cdc83 100644
--- a/wireless/bluetooth/bt_buf.c
+++ b/wireless/bluetooth/bt_buf.c
@@ -396,6 +396,8 @@ void bt_buf_release(FAR struct bt_buf_s *buf)
 
   wlinfo("buf %p ref %u type %d\n", buf, buf->ref, buf->type);
 
+  DEBUGASSERT(buf->ref > 0);
+
   if (--buf->ref > 0)
     {
       wlinfo("Remaining references: %d\n", buf->ref);