You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by an...@apache.org on 2022/09/14 10:52:39 UTC

[mynewt-nimble] 06/09: nimble/phy/nrf5x: Add nRF53 support

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

andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git

commit 9ea411fbe637ca4b0e40d32a9741e6ac8cb57a3f
Author: Andrzej Kaczmarek <an...@codecoup.pl>
AuthorDate: Thu Sep 8 12:13:15 2022 +0200

    nimble/phy/nrf5x: Add nRF53 support
    
    This adds code for nRF5340 net core.
---
 nimble/drivers/nrf5x/pkg.yml             |   5 +
 nimble/drivers/nrf5x/src/ble_phy.c       |  48 ++++++-
 nimble/drivers/nrf5x/src/nrf53/phy.c     | 235 +++++++++++++++++++++++++++++++
 nimble/drivers/nrf5x/src/nrf53/phy_ppi.h | 159 +++++++++++++++++++++
 nimble/drivers/nrf5x/src/phy_priv.h      |   8 ++
 5 files changed, 452 insertions(+), 3 deletions(-)

diff --git a/nimble/drivers/nrf5x/pkg.yml b/nimble/drivers/nrf5x/pkg.yml
index e0b314ea..b99e6692 100644
--- a/nimble/drivers/nrf5x/pkg.yml
+++ b/nimble/drivers/nrf5x/pkg.yml
@@ -29,3 +29,8 @@ pkg.apis: ble_driver
 pkg.deps:
     - nimble
     - nimble/controller
+
+pkg.ign_dirs.'MCU_TARGET=="nRF5340_NET"':
+    - nrf52
+pkg.ign_dirs.'MCU_TARGET!="nRF5340_NET"':
+    - nrf53
diff --git a/nimble/drivers/nrf5x/src/ble_phy.c b/nimble/drivers/nrf5x/src/ble_phy.c
index 36f209c5..0d9b363f 100644
--- a/nimble/drivers/nrf5x/src/ble_phy.c
+++ b/nimble/drivers/nrf5x/src/ble_phy.c
@@ -39,7 +39,12 @@
 #include "controller/ble_ll.h"
 #include "nrfx.h"
 #if MYNEWT
-#include "mcu/nrf52_clock.h"
+#ifdef NRF52_SERIES
+#include <mcu/nrf52_clock.h>
+#endif
+#ifdef NRF53_SERIES
+#include <mcu/nrf5340_net_clock.h>
+#endif
 #include "mcu/cmsis_nvic.h"
 #include "hal/hal_gpio.h"
 #else
@@ -49,8 +54,10 @@
 #include "phy_priv.h"
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
-#if !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52840) && !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52811)
-#error LE Coded PHY can only be enabled on nRF52811 or nRF52840
+#if !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52840) && \
+    !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52811) && \
+    !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF5340_NET)
+#error LE Coded PHY can only be enabled on nRF52811, nRF52840 or nRF5340
 #endif
 #endif
 
@@ -379,21 +386,33 @@ ble_phy_mode_apply(uint8_t phy_mode)
     switch (phy_mode) {
     case BLE_PHY_MODE_1M:
         NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_1Mbit;
+#ifdef NRF53_SERIES
+        *((volatile uint32_t *)0x41008588) = *((volatile uint32_t *)0x01FF0080);
+#endif
         NRF_RADIO->PCNF0 = NRF_PCNF0_1M;
         break;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY)
     case BLE_PHY_MODE_2M:
         NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_2Mbit;
+#ifdef NRF53_SERIES
+        *((volatile uint32_t *)0x41008588) = *((volatile uint32_t *)0x01FF0084);
+#endif
         NRF_RADIO->PCNF0 = NRF_PCNF0_2M;
         break;
 #endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
     case BLE_PHY_MODE_CODED_125KBPS:
         NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_LR125Kbit;
+#ifdef NRF53_SERIES
+        *((volatile uint32_t *)0x41008588) = *((volatile uint32_t *)0x01FF0080);
+#endif
         NRF_RADIO->PCNF0 = NRF_PCNF0_CODED;
         break;
     case BLE_PHY_MODE_CODED_500KBPS:
         NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_LR500Kbit;
+#ifdef NRF53_SERIES
+        *((volatile uint32_t *)0x41008588) = *((volatile uint32_t *)0x01FF0080);
+#endif
         NRF_RADIO->PCNF0 = NRF_PCNF0_CODED;
         break;
 #endif
@@ -1392,6 +1411,19 @@ ble_phy_init(void)
     nrf_radio_power_set(NRF_RADIO, false);
     nrf_radio_power_set(NRF_RADIO, true);
 
+#ifdef NRF53_SERIES
+    *(volatile uint32_t *)(NRF_RADIO_NS_BASE + 0x774) =
+        (*(volatile uint32_t* )(NRF_RADIO_NS_BASE + 0x774) & 0xfffffffe) | 0x01000000;
+#if NRF53_ERRATA_16_ENABLE_WORKAROUND
+    if (nrf53_errata_16()) {
+        /* [16] RADIO: POWER register is not functional */
+        NRF_RADIO_NS->SUBSCRIBE_TXEN = 0;
+        NRF_RADIO_NS->SUBSCRIBE_RXEN = 0;
+        NRF_RADIO_NS->SUBSCRIBE_DISABLE = 0;
+    }
+#endif
+#endif
+
     /* Disable all interrupts */
     nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_IRQ_MASK_ALL);
 
@@ -2071,7 +2103,12 @@ void
 ble_phy_rfclk_enable(void)
 {
 #if MYNEWT || defined(RIOT_VERSION)
+#ifdef NRF52_SERIES
     nrf52_clock_hfxo_request();
+#endif
+#ifdef NRF53_SERIES
+    nrf5340_net_clock_hfxo_request();
+#endif
 #else
     nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART);
 #endif
@@ -2081,7 +2118,12 @@ void
 ble_phy_rfclk_disable(void)
 {
 #if MYNEWT || defined(RIOT_VERSION)
+#ifdef NRF52_SERIES
     nrf52_clock_hfxo_release();
+#endif
+#ifdef NRF53_SERIES
+    nrf5340_net_clock_hfxo_release();
+#endif
 #else
     nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP);
 #endif
diff --git a/nimble/drivers/nrf5x/src/nrf53/phy.c b/nimble/drivers/nrf5x/src/nrf53/phy.c
new file mode 100644
index 00000000..78b45578
--- /dev/null
+++ b/nimble/drivers/nrf5x/src/nrf53/phy.c
@@ -0,0 +1,235 @@
+/*
+ * 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.
+ */
+
+#include <stdint.h>
+#include <nrfx.h>
+#include <controller/ble_ll_fem.h>
+#include "../phy_priv.h"
+
+#if PHY_USE_DEBUG
+void
+phy_debug_init(void)
+{
+#if PHY_USE_DEBUG_1
+    phy_gpiote_configure(PHY_GPIOTE_DEBUG_1,
+                         MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN));
+
+    NRF_RADIO->PUBLISH_READY = DPPI_CH_PUB(RADIO_EVENTS_READY);
+    NRF_DPPIC->CHENSET = DPPI_CH_MASK(RADIO_EVENTS_READY);
+
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_1] = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_0);
+    NRF_GPIOTE->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_1] = DPPI_CH_SUB(RADIO_EVENTS_READY);
+#endif
+
+#if PHY_USE_DEBUG_2
+    phy_gpiote_configure(PHY_GPIOTE_DEBUG_2,
+                         MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN));
+
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_2] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS);
+    NRF_GPIOTE->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_2] = DPPI_CH_SUB(RADIO_EVENTS_END);
+#endif
+
+#if PHY_USE_DEBUG_3
+    phy_gpiote_configure(PHY_GPIOTE_DEBUG_3, MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN));
+
+    NRF_RADIO->PUBLISH_RXREADY = DPPI_CH_PUB(RADIO_EVENTS_READY);
+    NRF_RADIO->PUBLISH_DISABLED = DPPI_CH_PUB(RADIO_EVENTS_DISABLED);
+    NRF_DPPIC->CHENSET = DPPI_CH_MASK(RADIO_EVENTS_RXREADY) |
+                         DPPI_CH_MASK(RADIO_EVENTS_DISABLED);
+
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_3] = DPPI_CH_SUB(RADIO_EVENTS_RXREADY);
+
+    /* TODO figure out how (if?) to subscribe task to multiple DPPI channels
+     * Currently only last one is working. Also using multiple GPIOTE for same
+     * PIN doesn't work...
+     */
+    NRF_GPIOTE->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_3] = DPPI_CH_SUB(RADIO_EVENTS_DISABLED);
+    NRF_GPIOTE->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_3] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS);
+    NRF_GPIOTE->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_3] = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_3);
+
+#endif
+}
+#endif /* PHY_USE_DEBUG */
+
+#if PHY_USE_FEM
+void
+phy_fem_init()
+{
+    /* We can keep clear tasks subscribed and published channels always enabled,
+     * it's enough to just (un)subscribe set tasks when needed.
+     * TODO: check if this affects power consumption
+     */
+
+    NRF_TIMER0->PUBLISH_COMPARE[2] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_2);
+    NRF_RADIO->PUBLISH_DISABLED = DPPI_CH_PUB(RADIO_EVENTS_DISABLED);
+
+#if PHY_USE_FEM_SINGLE_GPIO
+#if PHY_USE_FEM_PA
+    phy_gpiote_configure(PHY_GPIOTE_FEM, MYNEWT_VAL(BLE_LL_FEM_PA_GPIO));
+#else
+    phy_gpiote_configure(PHY_GPIOTE_FEM, MYNEWT_VAL(BLE_LL_FEM_LNA_GPIO));
+#endif
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM] =
+        DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2);
+    NRF_GPIOTE->SUBSCRIBE_CLR[PHY_GPIOTE_FEM] =
+        DPPI_CH_SUB(RADIO_EVENTS_DISABLED);
+    NRF_GPIOTE->TASKS_CLR[PHY_GPIOTE_FEM] = 1;
+#else
+#if PHY_USE_FEM_PA
+    phy_gpiote_configure(PHY_GPIOTE_FEM_PA, MYNEWT_VAL(BLE_LL_FEM_PA_GPIO));
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM_PA] =
+        DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2);
+    NRF_GPIOTE->SUBSCRIBE_CLR[PHY_GPIOTE_FEM_PA] =
+        DPPI_CH_SUB(RADIO_EVENTS_DISABLED);
+    NRF_GPIOTE->TASKS_CLR[PHY_GPIOTE_FEM_PA] = 1;
+#endif
+#if PHY_USE_FEM_LNA
+    phy_gpiote_configure(PHY_GPIOTE_FEM_LNA, MYNEWT_VAL(BLE_LL_FEM_LNA_GPIO));
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM_LNA] =
+        DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2);
+    NRF_GPIOTE->SUBSCRIBE_CLR[PHY_GPIOTE_FEM_LNA] =
+        DPPI_CH_SUB(RADIO_EVENTS_DISABLED);
+    NRF_GPIOTE->TASKS_CLR[PHY_GPIOTE_FEM_LNA] = 1;
+#endif
+#endif /* PHY_USE_FEM_SINGLE_GPIO */
+
+    NRF_DPPIC->CHENSET = DPPI_CH_MASK_FEM;
+}
+
+void
+phy_fem_enable_pa(void)
+{
+    ble_ll_fem_pa_enable();
+
+#if PHY_USE_FEM_SINGLE_GPIO
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM] =
+        DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_2);
+#else
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM_PA] =
+        DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_2);
+#endif
+}
+
+void
+phy_fem_enable_lna(void)
+{
+    ble_ll_fem_lna_enable();
+
+#if PHY_USE_FEM_SINGLE_GPIO
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM] =
+        DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_2);
+#else
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM_LNA] =
+        DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_2);
+#endif
+}
+
+void
+phy_fem_disable(void)
+{
+#if PHY_USE_FEM_SINGLE_GPIO
+    NRF_GPIOTE->TASKS_CLR[PHY_GPIOTE_FEM] = 1;
+#else
+#if PHY_USE_FEM_PA
+    NRF_GPIOTE->TASKS_CLR[PHY_GPIOTE_FEM_PA] = 1;
+#endif
+#if PHY_USE_FEM_LNA
+    NRF_GPIOTE->TASKS_CLR[PHY_GPIOTE_FEM_LNA] = 1;
+#endif
+#endif
+}
+#endif /* PHY_USE_FEM */
+
+void
+phy_ppi_init(void)
+{
+    /* Publish events */
+    NRF_TIMER0->PUBLISH_COMPARE[0] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_0);
+    NRF_TIMER0->PUBLISH_COMPARE[3] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_3);
+    NRF_RADIO->PUBLISH_END = DPPI_CH_PUB(RADIO_EVENTS_END);
+    NRF_RADIO->PUBLISH_BCMATCH = DPPI_CH_PUB(RADIO_EVENTS_BCMATCH);
+    NRF_RADIO->PUBLISH_ADDRESS = DPPI_CH_PUB(RADIO_EVENTS_ADDRESS);
+    NRF_RTC0->PUBLISH_COMPARE[0] = DPPI_CH_PUB(RTC0_EVENTS_COMPARE_0);
+
+    /* Enable channels we publish on */
+    NRF_DPPIC->CHENSET = DPPI_CH_ENABLE_ALL;
+
+    /* radio_address_to_timer0_capture1 */
+    NRF_TIMER0->SUBSCRIBE_CAPTURE[1] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS);
+    /* radio_end_to_timer0_capture2 */
+    NRF_TIMER0->SUBSCRIBE_CAPTURE[2] = DPPI_CH_SUB(RADIO_EVENTS_END);
+}
+
+int8_t
+phy_txpower_round(int8_t dbm)
+{
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_0dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_0dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg1dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg1dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg2dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg2dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg3dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg3dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg5dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg5dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg6dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg6dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg7dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg7dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg8dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg8dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg12dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg12dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg16dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg16dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg20dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg20dBm;
+    }
+
+    if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg40dBm) {
+        return (int8_t)RADIO_TXPOWER_TXPOWER_Neg40dBm;
+    }
+
+    return (int8_t)RADIO_TXPOWER_TXPOWER_Neg40dBm;
+}
diff --git a/nimble/drivers/nrf5x/src/nrf53/phy_ppi.h b/nimble/drivers/nrf5x/src/nrf53/phy_ppi.h
new file mode 100644
index 00000000..6412f327
--- /dev/null
+++ b/nimble/drivers/nrf5x/src/nrf53/phy_ppi.h
@@ -0,0 +1,159 @@
+/*
+ * 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 H_PHY_PPI_
+#define H_PHY_PPI_
+
+#define DPPI_CH_PUB(_ch)        (((DPPI_CH_ ## _ch) & 0xff) | (1 << 31))
+#define DPPI_CH_SUB(_ch)        (((DPPI_CH_ ## _ch) & 0xff) | (1 << 31))
+#define DPPI_CH_UNSUB(_ch)      (((DPPI_CH_ ## _ch) & 0xff) | (0 << 31))
+#define DPPI_CH_MASK(_ch)       (1 << (DPPI_CH_ ## _ch))
+
+/* Channels 0..5 are always used.
+ * Channels 6 and 7 are used for PA/LNA (optionally).
+ * Channels 7..9 are used for GPIO debugging (optionally).
+ */
+
+#define DPPI_CH_TIMER0_EVENTS_COMPARE_0         0
+#define DPPI_CH_TIMER0_EVENTS_COMPARE_3         1
+#define DPPI_CH_RADIO_EVENTS_END                2
+#define DPPI_CH_RADIO_EVENTS_BCMATCH            3
+#define DPPI_CH_RADIO_EVENTS_ADDRESS            4
+#define DPPI_CH_RTC0_EVENTS_COMPARE_0           5
+#define DPPI_CH_TIMER0_EVENTS_COMPARE_2         6
+#define DPPI_CH_RADIO_EVENTS_DISABLED           7
+#define DPPI_CH_RADIO_EVENTS_READY              8
+#define DPPI_CH_RADIO_EVENTS_RXREADY            9
+
+#define DPPI_CH_ENABLE_ALL  (DPPIC_CHEN_CH0_Msk | DPPIC_CHEN_CH1_Msk | \
+                             DPPIC_CHEN_CH2_Msk | DPPIC_CHEN_CH3_Msk | \
+                             DPPIC_CHEN_CH4_Msk | DPPIC_CHEN_CH5_Msk)
+
+#define DPPI_CH_MASK_FEM    (DPPI_CH_MASK(TIMER0_EVENTS_COMPARE_2) | \
+                             DPPI_CH_MASK(RADIO_EVENTS_DISABLED))
+
+static inline void
+phy_ppi_rtc0_compare0_to_timer0_start_enable(void)
+{
+    NRF_TIMER0->SUBSCRIBE_START = DPPI_CH_SUB(RTC0_EVENTS_COMPARE_0);
+}
+
+static inline void
+phy_ppi_rtc0_compare0_to_timer0_start_disable(void)
+{
+    NRF_TIMER0->SUBSCRIBE_START = DPPI_CH_UNSUB(RTC0_EVENTS_COMPARE_0);
+    NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS);
+    NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3);
+}
+
+static inline void
+phy_ppi_timer0_compare0_to_radio_txen_enable(void)
+{
+    NRF_RADIO->SUBSCRIBE_TXEN = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_0);
+}
+
+static inline void
+phy_ppi_timer0_compare0_to_radio_txen_disable(void)
+{
+    NRF_RADIO->SUBSCRIBE_TXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0);
+}
+
+static inline void
+phy_ppi_timer0_compare0_to_radio_rxen_enable(void)
+{
+    NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_0);
+}
+
+static inline void
+phy_ppi_timer0_compare0_to_radio_rxen_disable(void)
+{
+    NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0);
+}
+
+static inline void
+phy_ppi_radio_address_to_ccm_crypt_enable(void)
+{
+    NRF_CCM->SUBSCRIBE_CRYPT = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS);
+}
+
+static inline void
+phy_ppi_radio_address_to_ccm_crypt_disable(void)
+{
+    NRF_CCM->SUBSCRIBE_CRYPT = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS);
+}
+
+static inline void
+phy_ppi_radio_bcmatch_to_aar_start_enable(void)
+{
+    NRF_AAR->SUBSCRIBE_START = DPPI_CH_SUB(RADIO_EVENTS_BCMATCH);
+}
+
+static inline void
+phy_ppi_radio_bcmatch_to_aar_start_disable(void)
+{
+    NRF_AAR->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_BCMATCH);
+}
+
+static inline void
+phy_ppi_wfr_enable(void)
+{
+    NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS);
+    NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_3);
+}
+
+static inline void
+phy_ppi_wfr_disable(void)
+{
+    NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS);
+    NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3);
+}
+
+static inline void
+phy_ppi_fem_disable(void)
+{
+#if PHY_USE_FEM_SINGLE_GPIO
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM] =
+        DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3);
+#else
+#if PHY_USE_FEM_PA
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM_PA] =
+        DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2);
+#endif
+#if PHY_USE_FEM_LNA
+    NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM_LNA] =
+        DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2);
+#endif
+#endif
+}
+
+static inline void
+phy_ppi_disable(void)
+{
+    NRF_TIMER0->SUBSCRIBE_START = DPPI_CH_UNSUB(RTC0_EVENTS_COMPARE_0);
+    NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS);
+    NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3);
+    NRF_RADIO->SUBSCRIBE_TXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0);
+    NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0);
+    NRF_AAR->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_BCMATCH);
+    NRF_CCM->SUBSCRIBE_CRYPT = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS);
+
+    phy_ppi_fem_disable();
+}
+
+#endif /* H_PHY_PPI_ */
\ No newline at end of file
diff --git a/nimble/drivers/nrf5x/src/phy_priv.h b/nimble/drivers/nrf5x/src/phy_priv.h
index 1a026a6d..8cf140ee 100644
--- a/nimble/drivers/nrf5x/src/phy_priv.h
+++ b/nimble/drivers/nrf5x/src/phy_priv.h
@@ -20,6 +20,7 @@
 #ifndef H_PHY_PRIV_
 #define H_PHY_PRIV_
 
+#include <stdint.h>
 #include <nrf_gpio.h>
 #include <nrf_gpiote.h>
 
@@ -79,4 +80,11 @@ void phy_ppi_init(void);
 
 int8_t phy_txpower_round(int8_t dbm);
 
+#ifdef NRF52_SERIES
+#include "nrf52/phy_ppi.h"
+#endif
+#ifdef NRF53_SERIES
+#include "nrf53/phy_ppi.h"
+#endif
+
 #endif /* H_PHY_PRIV_ */